1 //
2 // Copyright 2020 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <grpc/support/port_platform.h>
18
19 #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
20
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/log.h>
23 #include <grpc/support/string_util.h>
24
25 #include "src/core/lib/gprpp/stat.h"
26 #include "src/core/lib/slice/slice_internal.h"
27 #include "src/core/lib/surface/api_trace.h"
28
29 namespace grpc_core {
30
StaticDataCertificateProvider(std::string root_certificate,grpc_core::PemKeyCertPairList pem_key_cert_pairs)31 StaticDataCertificateProvider::StaticDataCertificateProvider(
32 std::string root_certificate,
33 grpc_core::PemKeyCertPairList pem_key_cert_pairs)
34 : distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()),
35 root_certificate_(std::move(root_certificate)),
36 pem_key_cert_pairs_(std::move(pem_key_cert_pairs)) {
37 distributor_->SetWatchStatusCallback([this](std::string cert_name,
38 bool root_being_watched,
39 bool identity_being_watched) {
40 grpc_core::MutexLock lock(&mu_);
41 absl::optional<std::string> root_certificate;
42 absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
43 StaticDataCertificateProvider::WatcherInfo& info = watcher_info_[cert_name];
44 if (!info.root_being_watched && root_being_watched &&
45 !root_certificate_.empty()) {
46 root_certificate = root_certificate_;
47 }
48 info.root_being_watched = root_being_watched;
49 if (!info.identity_being_watched && identity_being_watched &&
50 !pem_key_cert_pairs_.empty()) {
51 pem_key_cert_pairs = pem_key_cert_pairs_;
52 }
53 info.identity_being_watched = identity_being_watched;
54 if (!info.root_being_watched && !info.identity_being_watched) {
55 watcher_info_.erase(cert_name);
56 }
57 const bool root_has_update = root_certificate.has_value();
58 const bool identity_has_update = pem_key_cert_pairs.has_value();
59 if (root_has_update || identity_has_update) {
60 distributor_->SetKeyMaterials(cert_name, std::move(root_certificate),
61 std::move(pem_key_cert_pairs));
62 }
63 grpc_error_handle root_cert_error = GRPC_ERROR_NONE;
64 grpc_error_handle identity_cert_error = GRPC_ERROR_NONE;
65 if (root_being_watched && !root_has_update) {
66 root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
67 "Unable to get latest root certificates.");
68 }
69 if (identity_being_watched && !identity_has_update) {
70 identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
71 "Unable to get latest identity certificates.");
72 }
73 if (root_cert_error != GRPC_ERROR_NONE ||
74 identity_cert_error != GRPC_ERROR_NONE) {
75 distributor_->SetErrorForCert(cert_name, root_cert_error,
76 identity_cert_error);
77 }
78 });
79 }
80
~StaticDataCertificateProvider()81 StaticDataCertificateProvider::~StaticDataCertificateProvider() {
82 // Reset distributor's callback to make sure the callback won't be invoked
83 // again after this object(provider) is destroyed.
84 distributor_->SetWatchStatusCallback(nullptr);
85 }
86
87 namespace {
88
TimeoutSecondsToDeadline(int64_t seconds)89 gpr_timespec TimeoutSecondsToDeadline(int64_t seconds) {
90 return gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
91 gpr_time_from_seconds(seconds, GPR_TIMESPAN));
92 }
93
94 } // namespace
95
FileWatcherCertificateProvider(std::string private_key_path,std::string identity_certificate_path,std::string root_cert_path,unsigned int refresh_interval_sec)96 FileWatcherCertificateProvider::FileWatcherCertificateProvider(
97 std::string private_key_path, std::string identity_certificate_path,
98 std::string root_cert_path, unsigned int refresh_interval_sec)
99 : private_key_path_(std::move(private_key_path)),
100 identity_certificate_path_(std::move(identity_certificate_path)),
101 root_cert_path_(std::move(root_cert_path)),
102 refresh_interval_sec_(refresh_interval_sec),
103 distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()) {
104 // Private key and identity cert files must be both set or both unset.
105 GPR_ASSERT(private_key_path_.empty() == identity_certificate_path_.empty());
106 // Must be watching either root or identity certs.
107 GPR_ASSERT(!private_key_path_.empty() || !root_cert_path_.empty());
108 gpr_event_init(&shutdown_event_);
109 ForceUpdate();
110 auto thread_lambda = [](void* arg) {
111 FileWatcherCertificateProvider* provider =
112 static_cast<FileWatcherCertificateProvider*>(arg);
113 GPR_ASSERT(provider != nullptr);
114 while (true) {
115 void* value = gpr_event_wait(
116 &provider->shutdown_event_,
117 TimeoutSecondsToDeadline(provider->refresh_interval_sec_));
118 if (value != nullptr) {
119 return;
120 };
121 provider->ForceUpdate();
122 }
123 };
124 refresh_thread_ = grpc_core::Thread(
125 "FileWatcherCertificateProvider_refreshing_thread", thread_lambda, this);
126 refresh_thread_.Start();
127 distributor_->SetWatchStatusCallback([this](std::string cert_name,
128 bool root_being_watched,
129 bool identity_being_watched) {
130 grpc_core::MutexLock lock(&mu_);
131 absl::optional<std::string> root_certificate;
132 absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
133 FileWatcherCertificateProvider::WatcherInfo& info =
134 watcher_info_[cert_name];
135 if (!info.root_being_watched && root_being_watched &&
136 !root_certificate_.empty()) {
137 root_certificate = root_certificate_;
138 }
139 info.root_being_watched = root_being_watched;
140 if (!info.identity_being_watched && identity_being_watched &&
141 !pem_key_cert_pairs_.empty()) {
142 pem_key_cert_pairs = pem_key_cert_pairs_;
143 }
144 info.identity_being_watched = identity_being_watched;
145 if (!info.root_being_watched && !info.identity_being_watched) {
146 watcher_info_.erase(cert_name);
147 }
148 ExecCtx exec_ctx;
149 if (root_certificate.has_value() || pem_key_cert_pairs.has_value()) {
150 distributor_->SetKeyMaterials(cert_name, root_certificate,
151 pem_key_cert_pairs);
152 }
153 grpc_error_handle root_cert_error = GRPC_ERROR_NONE;
154 grpc_error_handle identity_cert_error = GRPC_ERROR_NONE;
155 if (root_being_watched && !root_certificate.has_value()) {
156 root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
157 "Unable to get latest root certificates.");
158 }
159 if (identity_being_watched && !pem_key_cert_pairs.has_value()) {
160 identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
161 "Unable to get latest identity certificates.");
162 }
163 if (root_cert_error != GRPC_ERROR_NONE ||
164 identity_cert_error != GRPC_ERROR_NONE) {
165 distributor_->SetErrorForCert(cert_name, root_cert_error,
166 identity_cert_error);
167 }
168 });
169 }
170
~FileWatcherCertificateProvider()171 FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
172 // Reset distributor's callback to make sure the callback won't be invoked
173 // again after this object(provider) is destroyed.
174 distributor_->SetWatchStatusCallback(nullptr);
175 gpr_event_set(&shutdown_event_, reinterpret_cast<void*>(1));
176 refresh_thread_.Join();
177 }
178
ForceUpdate()179 void FileWatcherCertificateProvider::ForceUpdate() {
180 absl::optional<std::string> root_certificate;
181 absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
182 if (!root_cert_path_.empty()) {
183 root_certificate = ReadRootCertificatesFromFile(root_cert_path_);
184 }
185 if (!private_key_path_.empty()) {
186 pem_key_cert_pairs = ReadIdentityKeyCertPairFromFiles(
187 private_key_path_, identity_certificate_path_);
188 }
189 grpc_core::MutexLock lock(&mu_);
190 const bool root_cert_changed =
191 (!root_certificate.has_value() && !root_certificate_.empty()) ||
192 (root_certificate.has_value() && root_certificate_ != *root_certificate);
193 if (root_cert_changed) {
194 if (root_certificate.has_value()) {
195 root_certificate_ = std::move(*root_certificate);
196 } else {
197 root_certificate_ = "";
198 }
199 }
200 const bool identity_cert_changed =
201 (!pem_key_cert_pairs.has_value() && !pem_key_cert_pairs_.empty()) ||
202 (pem_key_cert_pairs.has_value() &&
203 pem_key_cert_pairs_ != *pem_key_cert_pairs);
204 if (identity_cert_changed) {
205 if (pem_key_cert_pairs.has_value()) {
206 pem_key_cert_pairs_ = std::move(*pem_key_cert_pairs);
207 } else {
208 pem_key_cert_pairs_ = {};
209 }
210 }
211 if (root_cert_changed || identity_cert_changed) {
212 ExecCtx exec_ctx;
213 grpc_error_handle root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
214 "Unable to get latest root certificates.");
215 grpc_error_handle identity_cert_error =
216 GRPC_ERROR_CREATE_FROM_STATIC_STRING(
217 "Unable to get latest identity certificates.");
218 for (const auto& p : watcher_info_) {
219 const std::string& cert_name = p.first;
220 const WatcherInfo& info = p.second;
221 absl::optional<std::string> root_to_report;
222 absl::optional<grpc_core::PemKeyCertPairList> identity_to_report;
223 // Set key materials to the distributor if their contents changed.
224 if (info.root_being_watched && !root_certificate_.empty() &&
225 root_cert_changed) {
226 root_to_report = root_certificate_;
227 }
228 if (info.identity_being_watched && !pem_key_cert_pairs_.empty() &&
229 identity_cert_changed) {
230 identity_to_report = pem_key_cert_pairs_;
231 }
232 if (root_to_report.has_value() || identity_to_report.has_value()) {
233 distributor_->SetKeyMaterials(cert_name, std::move(root_to_report),
234 std::move(identity_to_report));
235 }
236 // Report errors to the distributor if the contents are empty.
237 const bool report_root_error =
238 info.root_being_watched && root_certificate_.empty();
239 const bool report_identity_error =
240 info.identity_being_watched && pem_key_cert_pairs_.empty();
241 if (report_root_error || report_identity_error) {
242 distributor_->SetErrorForCert(
243 cert_name,
244 report_root_error ? GRPC_ERROR_REF(root_cert_error)
245 : GRPC_ERROR_NONE,
246 report_identity_error ? GRPC_ERROR_REF(identity_cert_error)
247 : GRPC_ERROR_NONE);
248 }
249 }
250 GRPC_ERROR_UNREF(root_cert_error);
251 GRPC_ERROR_UNREF(identity_cert_error);
252 }
253 }
254
255 absl::optional<std::string>
ReadRootCertificatesFromFile(const std::string & root_cert_full_path)256 FileWatcherCertificateProvider::ReadRootCertificatesFromFile(
257 const std::string& root_cert_full_path) {
258 // Read the root file.
259 grpc_slice root_slice = grpc_empty_slice();
260 grpc_error_handle root_error =
261 grpc_load_file(root_cert_full_path.c_str(), 0, &root_slice);
262 if (root_error != GRPC_ERROR_NONE) {
263 gpr_log(GPR_ERROR, "Reading file %s failed: %s",
264 root_cert_full_path.c_str(),
265 grpc_error_std_string(root_error).c_str());
266 GRPC_ERROR_UNREF(root_error);
267 return absl::nullopt;
268 }
269 std::string root_cert(StringViewFromSlice(root_slice));
270 grpc_slice_unref_internal(root_slice);
271 return root_cert;
272 }
273
274 namespace {
275
276 // This helper function gets the last-modified time of |filename|. When failed,
277 // it logs the error and returns 0.
GetModificationTime(const char * filename)278 time_t GetModificationTime(const char* filename) {
279 time_t ts = 0;
280 absl::Status status = grpc_core::GetFileModificationTime(filename, &ts);
281 return ts;
282 }
283
284 } // namespace
285
286 absl::optional<PemKeyCertPairList>
ReadIdentityKeyCertPairFromFiles(const std::string & private_key_path,const std::string & identity_certificate_path)287 FileWatcherCertificateProvider::ReadIdentityKeyCertPairFromFiles(
288 const std::string& private_key_path,
289 const std::string& identity_certificate_path) {
290 struct SliceWrapper {
291 grpc_slice slice = grpc_empty_slice();
292 ~SliceWrapper() { grpc_slice_unref_internal(slice); }
293 };
294 const int kNumRetryAttempts = 3;
295 for (int i = 0; i < kNumRetryAttempts; ++i) {
296 // TODO(ZhenLian): replace the timestamp approach with key-match approach
297 // once the latter is implemented.
298 // Checking the last modification of identity files before reading.
299 time_t identity_key_ts_before =
300 GetModificationTime(private_key_path.c_str());
301 if (identity_key_ts_before == 0) {
302 gpr_log(
303 GPR_ERROR,
304 "Failed to get the file's modification time of %s. Start retrying...",
305 private_key_path.c_str());
306 continue;
307 }
308 time_t identity_cert_ts_before =
309 GetModificationTime(identity_certificate_path.c_str());
310 if (identity_cert_ts_before == 0) {
311 gpr_log(
312 GPR_ERROR,
313 "Failed to get the file's modification time of %s. Start retrying...",
314 identity_certificate_path.c_str());
315 continue;
316 }
317 // Read the identity files.
318 SliceWrapper key_slice, cert_slice;
319 grpc_error_handle key_error =
320 grpc_load_file(private_key_path.c_str(), 0, &key_slice.slice);
321 if (key_error != GRPC_ERROR_NONE) {
322 gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
323 private_key_path.c_str(),
324 grpc_error_std_string(key_error).c_str());
325 GRPC_ERROR_UNREF(key_error);
326 continue;
327 }
328 grpc_error_handle cert_error =
329 grpc_load_file(identity_certificate_path.c_str(), 0, &cert_slice.slice);
330 if (cert_error != GRPC_ERROR_NONE) {
331 gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
332 identity_certificate_path.c_str(),
333 grpc_error_std_string(cert_error).c_str());
334 GRPC_ERROR_UNREF(cert_error);
335 continue;
336 }
337 std::string private_key(StringViewFromSlice(key_slice.slice));
338 std::string cert_chain(StringViewFromSlice(cert_slice.slice));
339 PemKeyCertPairList identity_pairs;
340 identity_pairs.emplace_back(private_key, cert_chain);
341 // Checking the last modification of identity files before reading.
342 time_t identity_key_ts_after =
343 GetModificationTime(private_key_path.c_str());
344 if (identity_key_ts_before != identity_key_ts_after) {
345 gpr_log(GPR_ERROR,
346 "Last modified time before and after reading %s is not the same. "
347 "Start retrying...",
348 private_key_path.c_str());
349 continue;
350 }
351 time_t identity_cert_ts_after =
352 GetModificationTime(identity_certificate_path.c_str());
353 if (identity_cert_ts_before != identity_cert_ts_after) {
354 gpr_log(GPR_ERROR,
355 "Last modified time before and after reading %s is not the same. "
356 "Start retrying...",
357 identity_certificate_path.c_str());
358 continue;
359 }
360 return identity_pairs;
361 }
362 gpr_log(GPR_ERROR,
363 "All retry attempts failed. Will try again after the next interval.");
364 return absl::nullopt;
365 }
366
367 } // namespace grpc_core
368
369 /** -- Wrapper APIs declared in grpc_security.h -- **/
370
grpc_tls_certificate_provider_static_data_create(const char * root_certificate,grpc_tls_identity_pairs * pem_key_cert_pairs)371 grpc_tls_certificate_provider* grpc_tls_certificate_provider_static_data_create(
372 const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs) {
373 GPR_ASSERT(root_certificate != nullptr || pem_key_cert_pairs != nullptr);
374 grpc_core::ExecCtx exec_ctx;
375 grpc_core::PemKeyCertPairList identity_pairs_core;
376 if (pem_key_cert_pairs != nullptr) {
377 identity_pairs_core = std::move(pem_key_cert_pairs->pem_key_cert_pairs);
378 delete pem_key_cert_pairs;
379 }
380 std::string root_cert_core;
381 if (root_certificate != nullptr) {
382 root_cert_core = root_certificate;
383 }
384 return new grpc_core::StaticDataCertificateProvider(
385 std::move(root_cert_core), std::move(identity_pairs_core));
386 }
387
388 grpc_tls_certificate_provider*
grpc_tls_certificate_provider_file_watcher_create(const char * private_key_path,const char * identity_certificate_path,const char * root_cert_path,unsigned int refresh_interval_sec)389 grpc_tls_certificate_provider_file_watcher_create(
390 const char* private_key_path, const char* identity_certificate_path,
391 const char* root_cert_path, unsigned int refresh_interval_sec) {
392 grpc_core::ExecCtx exec_ctx;
393 return new grpc_core::FileWatcherCertificateProvider(
394 private_key_path == nullptr ? "" : private_key_path,
395 identity_certificate_path == nullptr ? "" : identity_certificate_path,
396 root_cert_path == nullptr ? "" : root_cert_path, refresh_interval_sec);
397 }
398
grpc_tls_certificate_provider_release(grpc_tls_certificate_provider * provider)399 void grpc_tls_certificate_provider_release(
400 grpc_tls_certificate_provider* provider) {
401 GRPC_API_TRACE("grpc_tls_certificate_provider_release(provider=%p)", 1,
402 (provider));
403 grpc_core::ExecCtx exec_ctx;
404 if (provider != nullptr) provider->Unref();
405 }
406