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 <stdint.h>
22 #include <time.h>
23
24 #include <algorithm>
25 #include <utility>
26 #include <vector>
27
28 #include <openssl/bio.h>
29 #include <openssl/crypto.h>
30 #include <openssl/evp.h>
31 #include <openssl/pem.h>
32 #include <openssl/x509.h>
33
34 #include "absl/status/status.h"
35
36 #include <grpc/slice.h>
37 #include <grpc/support/log.h>
38 #include <grpc/support/time.h>
39
40 #include "src/core/lib/debug/trace.h"
41 #include "src/core/lib/gprpp/stat.h"
42 #include "src/core/lib/gprpp/status_helper.h"
43 #include "src/core/lib/iomgr/error.h"
44 #include "src/core/lib/iomgr/exec_ctx.h"
45 #include "src/core/lib/iomgr/load_file.h"
46 #include "src/core/lib/slice/slice.h"
47 #include "src/core/lib/slice/slice_internal.h"
48 #include "src/core/lib/surface/api_trace.h"
49
50 namespace grpc_core {
51
StaticDataCertificateProvider(std::string root_certificate,PemKeyCertPairList pem_key_cert_pairs)52 StaticDataCertificateProvider::StaticDataCertificateProvider(
53 std::string root_certificate, PemKeyCertPairList pem_key_cert_pairs)
54 : distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()),
55 root_certificate_(std::move(root_certificate)),
56 pem_key_cert_pairs_(std::move(pem_key_cert_pairs)) {
57 distributor_->SetWatchStatusCallback([this](std::string cert_name,
58 bool root_being_watched,
59 bool identity_being_watched) {
60 MutexLock lock(&mu_);
61 absl::optional<std::string> root_certificate;
62 absl::optional<PemKeyCertPairList> pem_key_cert_pairs;
63 StaticDataCertificateProvider::WatcherInfo& info = watcher_info_[cert_name];
64 if (!info.root_being_watched && root_being_watched &&
65 !root_certificate_.empty()) {
66 root_certificate = root_certificate_;
67 }
68 info.root_being_watched = root_being_watched;
69 if (!info.identity_being_watched && identity_being_watched &&
70 !pem_key_cert_pairs_.empty()) {
71 pem_key_cert_pairs = pem_key_cert_pairs_;
72 }
73 info.identity_being_watched = identity_being_watched;
74 if (!info.root_being_watched && !info.identity_being_watched) {
75 watcher_info_.erase(cert_name);
76 }
77 const bool root_has_update = root_certificate.has_value();
78 const bool identity_has_update = pem_key_cert_pairs.has_value();
79 if (root_has_update || identity_has_update) {
80 distributor_->SetKeyMaterials(cert_name, std::move(root_certificate),
81 std::move(pem_key_cert_pairs));
82 }
83 grpc_error_handle root_cert_error;
84 grpc_error_handle identity_cert_error;
85 if (root_being_watched && !root_has_update) {
86 root_cert_error =
87 GRPC_ERROR_CREATE("Unable to get latest root certificates.");
88 }
89 if (identity_being_watched && !identity_has_update) {
90 identity_cert_error =
91 GRPC_ERROR_CREATE("Unable to get latest identity certificates.");
92 }
93 if (!root_cert_error.ok() || !identity_cert_error.ok()) {
94 distributor_->SetErrorForCert(cert_name, root_cert_error,
95 identity_cert_error);
96 }
97 });
98 }
99
~StaticDataCertificateProvider()100 StaticDataCertificateProvider::~StaticDataCertificateProvider() {
101 // Reset distributor's callback to make sure the callback won't be invoked
102 // again after this object(provider) is destroyed.
103 distributor_->SetWatchStatusCallback(nullptr);
104 }
105
type() const106 UniqueTypeName StaticDataCertificateProvider::type() const {
107 static UniqueTypeName::Factory kFactory("StaticData");
108 return kFactory.Create();
109 }
110
111 namespace {
112
TimeoutSecondsToDeadline(int64_t seconds)113 gpr_timespec TimeoutSecondsToDeadline(int64_t seconds) {
114 return gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
115 gpr_time_from_seconds(seconds, GPR_TIMESPAN));
116 }
117
118 } // namespace
119
120 static constexpr int64_t kMinimumFileWatcherRefreshIntervalSeconds = 1;
121
FileWatcherCertificateProvider(std::string private_key_path,std::string identity_certificate_path,std::string root_cert_path,int64_t refresh_interval_sec)122 FileWatcherCertificateProvider::FileWatcherCertificateProvider(
123 std::string private_key_path, std::string identity_certificate_path,
124 std::string root_cert_path, int64_t refresh_interval_sec)
125 : private_key_path_(std::move(private_key_path)),
126 identity_certificate_path_(std::move(identity_certificate_path)),
127 root_cert_path_(std::move(root_cert_path)),
128 refresh_interval_sec_(refresh_interval_sec),
129 distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()) {
130 if (refresh_interval_sec_ < kMinimumFileWatcherRefreshIntervalSeconds) {
131 gpr_log(GPR_INFO,
132 "FileWatcherCertificateProvider refresh_interval_sec_ set to value "
133 "less than minimum. Overriding configured value to minimum.");
134 refresh_interval_sec_ = kMinimumFileWatcherRefreshIntervalSeconds;
135 }
136 // Private key and identity cert files must be both set or both unset.
137 GPR_ASSERT(private_key_path_.empty() == identity_certificate_path_.empty());
138 // Must be watching either root or identity certs.
139 GPR_ASSERT(!private_key_path_.empty() || !root_cert_path_.empty());
140 gpr_event_init(&shutdown_event_);
141 ForceUpdate();
142 auto thread_lambda = [](void* arg) {
143 FileWatcherCertificateProvider* provider =
144 static_cast<FileWatcherCertificateProvider*>(arg);
145 GPR_ASSERT(provider != nullptr);
146 while (true) {
147 void* value = gpr_event_wait(
148 &provider->shutdown_event_,
149 TimeoutSecondsToDeadline(provider->refresh_interval_sec_));
150 if (value != nullptr) {
151 return;
152 };
153 provider->ForceUpdate();
154 }
155 };
156 refresh_thread_ = Thread("FileWatcherCertificateProvider_refreshing_thread",
157 thread_lambda, this);
158 refresh_thread_.Start();
159 distributor_->SetWatchStatusCallback([this](std::string cert_name,
160 bool root_being_watched,
161 bool identity_being_watched) {
162 MutexLock lock(&mu_);
163 absl::optional<std::string> root_certificate;
164 absl::optional<PemKeyCertPairList> pem_key_cert_pairs;
165 FileWatcherCertificateProvider::WatcherInfo& info =
166 watcher_info_[cert_name];
167 if (!info.root_being_watched && root_being_watched &&
168 !root_certificate_.empty()) {
169 root_certificate = root_certificate_;
170 }
171 info.root_being_watched = root_being_watched;
172 if (!info.identity_being_watched && identity_being_watched &&
173 !pem_key_cert_pairs_.empty()) {
174 pem_key_cert_pairs = pem_key_cert_pairs_;
175 }
176 info.identity_being_watched = identity_being_watched;
177 if (!info.root_being_watched && !info.identity_being_watched) {
178 watcher_info_.erase(cert_name);
179 }
180 ExecCtx exec_ctx;
181 if (root_certificate.has_value() || pem_key_cert_pairs.has_value()) {
182 distributor_->SetKeyMaterials(cert_name, root_certificate,
183 pem_key_cert_pairs);
184 }
185 grpc_error_handle root_cert_error;
186 grpc_error_handle identity_cert_error;
187 if (root_being_watched && !root_certificate.has_value()) {
188 root_cert_error =
189 GRPC_ERROR_CREATE("Unable to get latest root certificates.");
190 }
191 if (identity_being_watched && !pem_key_cert_pairs.has_value()) {
192 identity_cert_error =
193 GRPC_ERROR_CREATE("Unable to get latest identity certificates.");
194 }
195 if (!root_cert_error.ok() || !identity_cert_error.ok()) {
196 distributor_->SetErrorForCert(cert_name, root_cert_error,
197 identity_cert_error);
198 }
199 });
200 }
201
~FileWatcherCertificateProvider()202 FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
203 // Reset distributor's callback to make sure the callback won't be invoked
204 // again after this object(provider) is destroyed.
205 distributor_->SetWatchStatusCallback(nullptr);
206 gpr_event_set(&shutdown_event_, reinterpret_cast<void*>(1));
207 refresh_thread_.Join();
208 }
209
type() const210 UniqueTypeName FileWatcherCertificateProvider::type() const {
211 static UniqueTypeName::Factory kFactory("FileWatcher");
212 return kFactory.Create();
213 }
214
ForceUpdate()215 void FileWatcherCertificateProvider::ForceUpdate() {
216 absl::optional<std::string> root_certificate;
217 absl::optional<PemKeyCertPairList> pem_key_cert_pairs;
218 if (!root_cert_path_.empty()) {
219 root_certificate = ReadRootCertificatesFromFile(root_cert_path_);
220 }
221 if (!private_key_path_.empty()) {
222 pem_key_cert_pairs = ReadIdentityKeyCertPairFromFiles(
223 private_key_path_, identity_certificate_path_);
224 }
225 MutexLock lock(&mu_);
226 const bool root_cert_changed =
227 (!root_certificate.has_value() && !root_certificate_.empty()) ||
228 (root_certificate.has_value() && root_certificate_ != *root_certificate);
229 if (root_cert_changed) {
230 if (root_certificate.has_value()) {
231 root_certificate_ = std::move(*root_certificate);
232 } else {
233 root_certificate_ = "";
234 }
235 }
236 const bool identity_cert_changed =
237 (!pem_key_cert_pairs.has_value() && !pem_key_cert_pairs_.empty()) ||
238 (pem_key_cert_pairs.has_value() &&
239 pem_key_cert_pairs_ != *pem_key_cert_pairs);
240 if (identity_cert_changed) {
241 if (pem_key_cert_pairs.has_value()) {
242 pem_key_cert_pairs_ = std::move(*pem_key_cert_pairs);
243 } else {
244 pem_key_cert_pairs_ = {};
245 }
246 }
247 if (root_cert_changed || identity_cert_changed) {
248 ExecCtx exec_ctx;
249 grpc_error_handle root_cert_error =
250 GRPC_ERROR_CREATE("Unable to get latest root certificates.");
251 grpc_error_handle identity_cert_error =
252 GRPC_ERROR_CREATE("Unable to get latest identity certificates.");
253 for (const auto& p : watcher_info_) {
254 const std::string& cert_name = p.first;
255 const WatcherInfo& info = p.second;
256 absl::optional<std::string> root_to_report;
257 absl::optional<PemKeyCertPairList> identity_to_report;
258 // Set key materials to the distributor if their contents changed.
259 if (info.root_being_watched && !root_certificate_.empty() &&
260 root_cert_changed) {
261 root_to_report = root_certificate_;
262 }
263 if (info.identity_being_watched && !pem_key_cert_pairs_.empty() &&
264 identity_cert_changed) {
265 identity_to_report = pem_key_cert_pairs_;
266 }
267 if (root_to_report.has_value() || identity_to_report.has_value()) {
268 distributor_->SetKeyMaterials(cert_name, std::move(root_to_report),
269 std::move(identity_to_report));
270 }
271 // Report errors to the distributor if the contents are empty.
272 const bool report_root_error =
273 info.root_being_watched && root_certificate_.empty();
274 const bool report_identity_error =
275 info.identity_being_watched && pem_key_cert_pairs_.empty();
276 if (report_root_error || report_identity_error) {
277 distributor_->SetErrorForCert(
278 cert_name, report_root_error ? root_cert_error : absl::OkStatus(),
279 report_identity_error ? identity_cert_error : absl::OkStatus());
280 }
281 }
282 }
283 }
284
285 absl::optional<std::string>
ReadRootCertificatesFromFile(const std::string & root_cert_full_path)286 FileWatcherCertificateProvider::ReadRootCertificatesFromFile(
287 const std::string& root_cert_full_path) {
288 // Read the root file.
289 grpc_slice root_slice = grpc_empty_slice();
290 grpc_error_handle root_error =
291 grpc_load_file(root_cert_full_path.c_str(), 0, &root_slice);
292 if (!root_error.ok()) {
293 gpr_log(GPR_ERROR, "Reading file %s failed: %s",
294 root_cert_full_path.c_str(), StatusToString(root_error).c_str());
295 return absl::nullopt;
296 }
297 std::string root_cert(StringViewFromSlice(root_slice));
298 CSliceUnref(root_slice);
299 return root_cert;
300 }
301
302 namespace {
303
304 // This helper function gets the last-modified time of |filename|. When failed,
305 // it logs the error and returns 0.
GetModificationTime(const char * filename)306 time_t GetModificationTime(const char* filename) {
307 time_t ts = 0;
308 (void)GetFileModificationTime(filename, &ts);
309 return ts;
310 }
311
312 } // namespace
313
314 absl::optional<PemKeyCertPairList>
ReadIdentityKeyCertPairFromFiles(const std::string & private_key_path,const std::string & identity_certificate_path)315 FileWatcherCertificateProvider::ReadIdentityKeyCertPairFromFiles(
316 const std::string& private_key_path,
317 const std::string& identity_certificate_path) {
318 struct SliceWrapper {
319 grpc_slice slice = grpc_empty_slice();
320 ~SliceWrapper() { CSliceUnref(slice); }
321 };
322 const int kNumRetryAttempts = 3;
323 for (int i = 0; i < kNumRetryAttempts; ++i) {
324 // TODO(ZhenLian): replace the timestamp approach with key-match approach
325 // once the latter is implemented.
326 // Checking the last modification of identity files before reading.
327 time_t identity_key_ts_before =
328 GetModificationTime(private_key_path.c_str());
329 if (identity_key_ts_before == 0) {
330 gpr_log(
331 GPR_ERROR,
332 "Failed to get the file's modification time of %s. Start retrying...",
333 private_key_path.c_str());
334 continue;
335 }
336 time_t identity_cert_ts_before =
337 GetModificationTime(identity_certificate_path.c_str());
338 if (identity_cert_ts_before == 0) {
339 gpr_log(
340 GPR_ERROR,
341 "Failed to get the file's modification time of %s. Start retrying...",
342 identity_certificate_path.c_str());
343 continue;
344 }
345 // Read the identity files.
346 SliceWrapper key_slice, cert_slice;
347 grpc_error_handle key_error =
348 grpc_load_file(private_key_path.c_str(), 0, &key_slice.slice);
349 if (!key_error.ok()) {
350 gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
351 private_key_path.c_str(), StatusToString(key_error).c_str());
352 continue;
353 }
354 grpc_error_handle cert_error =
355 grpc_load_file(identity_certificate_path.c_str(), 0, &cert_slice.slice);
356 if (!cert_error.ok()) {
357 gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
358 identity_certificate_path.c_str(),
359 StatusToString(cert_error).c_str());
360 continue;
361 }
362 std::string private_key(StringViewFromSlice(key_slice.slice));
363 std::string cert_chain(StringViewFromSlice(cert_slice.slice));
364 PemKeyCertPairList identity_pairs;
365 identity_pairs.emplace_back(private_key, cert_chain);
366 // Checking the last modification of identity files before reading.
367 time_t identity_key_ts_after =
368 GetModificationTime(private_key_path.c_str());
369 if (identity_key_ts_before != identity_key_ts_after) {
370 gpr_log(GPR_ERROR,
371 "Last modified time before and after reading %s is not the same. "
372 "Start retrying...",
373 private_key_path.c_str());
374 continue;
375 }
376 time_t identity_cert_ts_after =
377 GetModificationTime(identity_certificate_path.c_str());
378 if (identity_cert_ts_before != identity_cert_ts_after) {
379 gpr_log(GPR_ERROR,
380 "Last modified time before and after reading %s is not the same. "
381 "Start retrying...",
382 identity_certificate_path.c_str());
383 continue;
384 }
385 return identity_pairs;
386 }
387 gpr_log(GPR_ERROR,
388 "All retry attempts failed. Will try again after the next interval.");
389 return absl::nullopt;
390 }
391
TestOnlyGetRefreshIntervalSecond() const392 int64_t FileWatcherCertificateProvider::TestOnlyGetRefreshIntervalSecond()
393 const {
394 return refresh_interval_sec_;
395 }
396
PrivateKeyAndCertificateMatch(absl::string_view private_key,absl::string_view cert_chain)397 absl::StatusOr<bool> PrivateKeyAndCertificateMatch(
398 absl::string_view private_key, absl::string_view cert_chain) {
399 if (private_key.empty()) {
400 return absl::InvalidArgumentError("Private key string is empty.");
401 }
402 if (cert_chain.empty()) {
403 return absl::InvalidArgumentError("Certificate string is empty.");
404 }
405 BIO* cert_bio =
406 BIO_new_mem_buf(cert_chain.data(), static_cast<int>(cert_chain.size()));
407 if (cert_bio == nullptr) {
408 return absl::InvalidArgumentError(
409 "Conversion from certificate string to BIO failed.");
410 }
411 // Reads the first cert from the cert_chain which is expected to be the leaf
412 // cert
413 X509* x509 = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
414 BIO_free(cert_bio);
415 if (x509 == nullptr) {
416 return absl::InvalidArgumentError(
417 "Conversion from PEM string to X509 failed.");
418 }
419 EVP_PKEY* public_evp_pkey = X509_get_pubkey(x509);
420 X509_free(x509);
421 if (public_evp_pkey == nullptr) {
422 return absl::InvalidArgumentError(
423 "Extraction of public key from x.509 certificate failed.");
424 }
425 BIO* private_key_bio =
426 BIO_new_mem_buf(private_key.data(), static_cast<int>(private_key.size()));
427 if (private_key_bio == nullptr) {
428 EVP_PKEY_free(public_evp_pkey);
429 return absl::InvalidArgumentError(
430 "Conversion from private key string to BIO failed.");
431 }
432 EVP_PKEY* private_evp_pkey =
433 PEM_read_bio_PrivateKey(private_key_bio, nullptr, nullptr, nullptr);
434 BIO_free(private_key_bio);
435 if (private_evp_pkey == nullptr) {
436 EVP_PKEY_free(public_evp_pkey);
437 return absl::InvalidArgumentError(
438 "Conversion from PEM string to EVP_PKEY failed.");
439 }
440 bool result = EVP_PKEY_cmp(private_evp_pkey, public_evp_pkey) == 1;
441 EVP_PKEY_free(private_evp_pkey);
442 EVP_PKEY_free(public_evp_pkey);
443 return result;
444 }
445
446 } // namespace grpc_core
447
448 /// -- Wrapper APIs declared in grpc_security.h -- *
449
grpc_tls_certificate_provider_static_data_create(const char * root_certificate,grpc_tls_identity_pairs * pem_key_cert_pairs)450 grpc_tls_certificate_provider* grpc_tls_certificate_provider_static_data_create(
451 const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs) {
452 GPR_ASSERT(root_certificate != nullptr || pem_key_cert_pairs != nullptr);
453 grpc_core::ExecCtx exec_ctx;
454 grpc_core::PemKeyCertPairList identity_pairs_core;
455 if (pem_key_cert_pairs != nullptr) {
456 identity_pairs_core = std::move(pem_key_cert_pairs->pem_key_cert_pairs);
457 delete pem_key_cert_pairs;
458 }
459 std::string root_cert_core;
460 if (root_certificate != nullptr) {
461 root_cert_core = root_certificate;
462 }
463 return new grpc_core::StaticDataCertificateProvider(
464 std::move(root_cert_core), std::move(identity_pairs_core));
465 }
466
467 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)468 grpc_tls_certificate_provider_file_watcher_create(
469 const char* private_key_path, const char* identity_certificate_path,
470 const char* root_cert_path, unsigned int refresh_interval_sec) {
471 grpc_core::ExecCtx exec_ctx;
472 return new grpc_core::FileWatcherCertificateProvider(
473 private_key_path == nullptr ? "" : private_key_path,
474 identity_certificate_path == nullptr ? "" : identity_certificate_path,
475 root_cert_path == nullptr ? "" : root_cert_path, refresh_interval_sec);
476 }
477
grpc_tls_certificate_provider_release(grpc_tls_certificate_provider * provider)478 void grpc_tls_certificate_provider_release(
479 grpc_tls_certificate_provider* provider) {
480 GRPC_API_TRACE("grpc_tls_certificate_provider_release(provider=%p)", 1,
481 (provider));
482 grpc_core::ExecCtx exec_ctx;
483 if (provider != nullptr) provider->Unref();
484 }
485