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