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::kReplication
32
33 #include "mongo/platform/basic.h"
34
35 #include "mongo/db/logical_time_validator.h"
36
37 #include "mongo/base/init.h"
38 #include "mongo/db/auth/action_set.h"
39 #include "mongo/db/auth/action_type.h"
40 #include "mongo/db/auth/authorization_session.h"
41 #include "mongo/db/auth/privilege.h"
42 #include "mongo/db/keys_collection_manager_sharding.h"
43 #include "mongo/db/logical_clock.h"
44 #include "mongo/db/operation_context.h"
45 #include "mongo/db/service_context.h"
46 #include "mongo/util/assert_util.h"
47 #include "mongo/util/log.h"
48
49 namespace mongo {
50
51 namespace {
52 const auto getLogicalClockValidator =
53 ServiceContext::declareDecoration<std::unique_ptr<LogicalTimeValidator>>();
54
55 stdx::mutex validatorMutex; // protects access to decoration instance of LogicalTimeValidator.
56
57 std::vector<Privilege> advanceClusterTimePrivilege;
58
MONGO_INITIALIZER(InitializeAdvanceClusterTimePrivilegeVector)59 MONGO_INITIALIZER(InitializeAdvanceClusterTimePrivilegeVector)(InitializerContext* const) {
60 ActionSet actions;
61 actions.addAction(ActionType::advanceClusterTime);
62 advanceClusterTimePrivilege.emplace_back(ResourcePattern::forClusterResource(), actions);
63 return Status::OK();
64 }
65
66 Milliseconds kRefreshIntervalIfErrored(200);
67
68 } // unnamed namespace
69
get(ServiceContext * service)70 LogicalTimeValidator* LogicalTimeValidator::get(ServiceContext* service) {
71 stdx::lock_guard<stdx::mutex> lk(validatorMutex);
72 return getLogicalClockValidator(service).get();
73 }
74
get(OperationContext * ctx)75 LogicalTimeValidator* LogicalTimeValidator::get(OperationContext* ctx) {
76 return get(ctx->getClient()->getServiceContext());
77 }
78
set(ServiceContext * service,std::unique_ptr<LogicalTimeValidator> newValidator)79 void LogicalTimeValidator::set(ServiceContext* service,
80 std::unique_ptr<LogicalTimeValidator> newValidator) {
81 stdx::lock_guard<stdx::mutex> lk(validatorMutex);
82 auto& validator = getLogicalClockValidator(service);
83 validator = std::move(newValidator);
84 }
85
LogicalTimeValidator(std::shared_ptr<KeysCollectionManagerSharding> keyManager)86 LogicalTimeValidator::LogicalTimeValidator(
87 std::shared_ptr<KeysCollectionManagerSharding> keyManager)
88 : _keyManager(keyManager) {}
89
_getProof(const KeysCollectionDocument & keyDoc,LogicalTime newTime)90 SignedLogicalTime LogicalTimeValidator::_getProof(const KeysCollectionDocument& keyDoc,
91 LogicalTime newTime) {
92 auto key = keyDoc.getKey();
93
94 // Compare and calculate HMAC inside mutex to prevent multiple threads computing HMAC for the
95 // same cluster time.
96 stdx::lock_guard<stdx::mutex> lk(_mutex);
97 // Note: _lastSeenValidTime will initially not have a proof set.
98 if (newTime == _lastSeenValidTime.getTime() && _lastSeenValidTime.getProof()) {
99 return _lastSeenValidTime;
100 }
101
102 auto signature = _timeProofService.getProof(newTime, key);
103 SignedLogicalTime newSignedTime(newTime, std::move(signature), keyDoc.getKeyId());
104
105 if (newTime > _lastSeenValidTime.getTime() || !_lastSeenValidTime.getProof()) {
106 _lastSeenValidTime = newSignedTime;
107 }
108
109 return newSignedTime;
110 }
111
trySignLogicalTime(const LogicalTime & newTime)112 SignedLogicalTime LogicalTimeValidator::trySignLogicalTime(const LogicalTime& newTime) {
113 auto keyStatusWith = _getKeyManagerCopy()->getKeyForSigning(nullptr, newTime);
114 auto keyStatus = keyStatusWith.getStatus();
115
116 if (keyStatus == ErrorCodes::KeyNotFound) {
117 // Attach invalid signature and keyId if we don't have the right keys to sign it.
118 return SignedLogicalTime(newTime, TimeProofService::TimeProof(), 0);
119 }
120
121 uassertStatusOK(keyStatus);
122 return _getProof(keyStatusWith.getValue(), newTime);
123 }
124
signLogicalTime(OperationContext * opCtx,const LogicalTime & newTime)125 SignedLogicalTime LogicalTimeValidator::signLogicalTime(OperationContext* opCtx,
126 const LogicalTime& newTime) {
127 auto keyManager = _getKeyManagerCopy();
128 auto keyStatusWith = keyManager->getKeyForSigning(nullptr, newTime);
129 auto keyStatus = keyStatusWith.getStatus();
130
131 while (keyStatus == ErrorCodes::KeyNotFound) {
132 keyManager->refreshNow(opCtx);
133
134 keyStatusWith = keyManager->getKeyForSigning(nullptr, newTime);
135 keyStatus = keyStatusWith.getStatus();
136
137 if (keyStatus == ErrorCodes::KeyNotFound) {
138 sleepFor(kRefreshIntervalIfErrored);
139 }
140 }
141
142 uassertStatusOK(keyStatus);
143 return _getProof(keyStatusWith.getValue(), newTime);
144 }
145
validate(OperationContext * opCtx,const SignedLogicalTime & newTime)146 Status LogicalTimeValidator::validate(OperationContext* opCtx, const SignedLogicalTime& newTime) {
147 {
148 stdx::lock_guard<stdx::mutex> lk(_mutex);
149 if (newTime.getTime() <= _lastSeenValidTime.getTime()) {
150 return Status::OK();
151 }
152 }
153
154 auto keyStatus =
155 _getKeyManagerCopy()->getKeyForValidation(opCtx, newTime.getKeyId(), newTime.getTime());
156 uassertStatusOK(keyStatus.getStatus());
157
158 const auto& key = keyStatus.getValue().getKey();
159
160 const auto newProof = newTime.getProof();
161 // Cluster time is only sent if a server's clock can verify and sign cluster times, so any
162 // received cluster times should have proofs.
163 invariant(newProof);
164
165 auto res = _timeProofService.checkProof(newTime.getTime(), newProof.get(), key);
166 if (res != Status::OK()) {
167 return res;
168 }
169
170 return Status::OK();
171 }
172
init(ServiceContext * service)173 void LogicalTimeValidator::init(ServiceContext* service) {
174 _getKeyManagerCopy()->startMonitoring(service);
175 }
176
shutDown()177 void LogicalTimeValidator::shutDown() {
178 stdx::lock_guard<stdx::mutex> lk(_mutexKeyManager);
179 if (_keyManager) {
180 _keyManager->stopMonitoring();
181 }
182 }
183
enableKeyGenerator(OperationContext * opCtx,bool doEnable)184 void LogicalTimeValidator::enableKeyGenerator(OperationContext* opCtx, bool doEnable) {
185 _getKeyManagerCopy()->enableKeyGenerator(opCtx, doEnable);
186 }
187
isAuthorizedToAdvanceClock(OperationContext * opCtx)188 bool LogicalTimeValidator::isAuthorizedToAdvanceClock(OperationContext* opCtx) {
189 auto client = opCtx->getClient();
190 // Note: returns true if auth is off, courtesy of
191 // AuthzSessionExternalStateServerCommon::shouldIgnoreAuthChecks.
192 return AuthorizationSession::get(client)->isAuthorizedForPrivileges(
193 advanceClusterTimePrivilege);
194 }
195
shouldGossipLogicalTime()196 bool LogicalTimeValidator::shouldGossipLogicalTime() {
197 return _getKeyManagerCopy()->hasSeenKeys();
198 }
199
forceKeyRefreshNow(OperationContext * opCtx)200 void LogicalTimeValidator::forceKeyRefreshNow(OperationContext* opCtx) {
201 _getKeyManagerCopy()->refreshNow(opCtx);
202 }
203
resetKeyManagerCache()204 void LogicalTimeValidator::resetKeyManagerCache() {
205 log() << "Resetting key manager cache";
206 // TODO: SERVER-34445
207 _getKeyManagerCopy()->clearCache();
208 _lastSeenValidTime = SignedLogicalTime();
209 _timeProofService.resetCache();
210 }
211
stopKeyManager()212 void LogicalTimeValidator::stopKeyManager() {
213 stdx::lock_guard<stdx::mutex> keyManagerLock(_mutexKeyManager);
214 if (_keyManager) {
215 log() << "Stopping key manager";
216 _keyManager->stopMonitoring();
217 _keyManager->clearCache();
218
219 stdx::lock_guard<stdx::mutex> lk(_mutex);
220 _lastSeenValidTime = SignedLogicalTime();
221 _timeProofService.resetCache();
222 } else {
223 log() << "Stopping key manager: no key manager exists.";
224 }
225 }
226
_getKeyManagerCopy()227 std::shared_ptr<KeysCollectionManagerSharding> LogicalTimeValidator::_getKeyManagerCopy() {
228 stdx::lock_guard<stdx::mutex> lk(_mutexKeyManager);
229 invariant(_keyManager);
230 return _keyManager;
231 }
232
233 } // namespace mongo
234