2 // Copyright 2020 gRPC authors.
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
8 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <grpc/support/port_platform.h>
19 #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/log.h>
23 #include <grpc/support/string_util.h>
24 #include <openssl/ssl.h>
26 #include "src/core/lib/gprpp/stat.h"
27 #include "src/core/lib/slice/slice_internal.h"
28 #include "src/core/lib/surface/api_trace.h"
32 StaticDataCertificateProvider::StaticDataCertificateProvider(
33 std::string root_certificate,
34 grpc_core::PemKeyCertPairList pem_key_cert_pairs)
35 : distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()),
36 root_certificate_(std::move(root_certificate)),
37 pem_key_cert_pairs_(std::move(pem_key_cert_pairs)) {
38 distributor_->SetWatchStatusCallback([this](std::string cert_name,
39 bool root_being_watched,
40 bool identity_being_watched) {
41 grpc_core::MutexLock lock(&mu_);
42 absl::optional<std::string> root_certificate;
43 absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
44 StaticDataCertificateProvider::WatcherInfo& info = watcher_info_[cert_name];
45 if (!info.root_being_watched && root_being_watched &&
46 !root_certificate_.empty()) {
47 root_certificate = root_certificate_;
49 info.root_being_watched = root_being_watched;
50 if (!info.identity_being_watched && identity_being_watched &&
51 !pem_key_cert_pairs_.empty()) {
52 pem_key_cert_pairs = pem_key_cert_pairs_;
54 info.identity_being_watched = identity_being_watched;
55 if (!info.root_being_watched && !info.identity_being_watched) {
56 watcher_info_.erase(cert_name);
58 const bool root_has_update = root_certificate.has_value();
59 const bool identity_has_update = pem_key_cert_pairs.has_value();
60 if (root_has_update || identity_has_update) {
61 distributor_->SetKeyMaterials(cert_name, std::move(root_certificate),
62 std::move(pem_key_cert_pairs));
64 grpc_error_handle root_cert_error = GRPC_ERROR_NONE;
65 grpc_error_handle identity_cert_error = GRPC_ERROR_NONE;
66 if (root_being_watched && !root_has_update) {
67 root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
68 "Unable to get latest root certificates.");
70 if (identity_being_watched && !identity_has_update) {
71 identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
72 "Unable to get latest identity certificates.");
74 if (root_cert_error != GRPC_ERROR_NONE ||
75 identity_cert_error != GRPC_ERROR_NONE) {
76 distributor_->SetErrorForCert(cert_name, root_cert_error,
82 StaticDataCertificateProvider::~StaticDataCertificateProvider() {
83 // Reset distributor's callback to make sure the callback won't be invoked
84 // again after this object(provider) is destroyed.
85 distributor_->SetWatchStatusCallback(nullptr);
90 gpr_timespec TimeoutSecondsToDeadline(int64_t seconds) {
91 return gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
92 gpr_time_from_seconds(seconds, GPR_TIMESPAN));
97 FileWatcherCertificateProvider::FileWatcherCertificateProvider(
98 std::string private_key_path, std::string identity_certificate_path,
99 std::string root_cert_path, unsigned int refresh_interval_sec)
100 : private_key_path_(std::move(private_key_path)),
101 identity_certificate_path_(std::move(identity_certificate_path)),
102 root_cert_path_(std::move(root_cert_path)),
103 refresh_interval_sec_(refresh_interval_sec),
104 distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()) {
105 // Private key and identity cert files must be both set or both unset.
106 GPR_ASSERT(private_key_path_.empty() == identity_certificate_path_.empty());
107 // Must be watching either root or identity certs.
108 GPR_ASSERT(!private_key_path_.empty() || !root_cert_path_.empty());
109 gpr_event_init(&shutdown_event_);
111 auto thread_lambda = [](void* arg) {
112 FileWatcherCertificateProvider* provider =
113 static_cast<FileWatcherCertificateProvider*>(arg);
114 GPR_ASSERT(provider != nullptr);
116 void* value = gpr_event_wait(
117 &provider->shutdown_event_,
118 TimeoutSecondsToDeadline(provider->refresh_interval_sec_));
119 if (value != nullptr) {
122 provider->ForceUpdate();
125 refresh_thread_ = grpc_core::Thread(
126 "FileWatcherCertificateProvider_refreshing_thread", thread_lambda, this);
127 refresh_thread_.Start();
128 distributor_->SetWatchStatusCallback([this](std::string cert_name,
129 bool root_being_watched,
130 bool identity_being_watched) {
131 grpc_core::MutexLock lock(&mu_);
132 absl::optional<std::string> root_certificate;
133 absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
134 FileWatcherCertificateProvider::WatcherInfo& info =
135 watcher_info_[cert_name];
136 if (!info.root_being_watched && root_being_watched &&
137 !root_certificate_.empty()) {
138 root_certificate = root_certificate_;
140 info.root_being_watched = root_being_watched;
141 if (!info.identity_being_watched && identity_being_watched &&
142 !pem_key_cert_pairs_.empty()) {
143 pem_key_cert_pairs = pem_key_cert_pairs_;
145 info.identity_being_watched = identity_being_watched;
146 if (!info.root_being_watched && !info.identity_being_watched) {
147 watcher_info_.erase(cert_name);
150 if (root_certificate.has_value() || pem_key_cert_pairs.has_value()) {
151 distributor_->SetKeyMaterials(cert_name, root_certificate,
154 grpc_error_handle root_cert_error = GRPC_ERROR_NONE;
155 grpc_error_handle identity_cert_error = GRPC_ERROR_NONE;
156 if (root_being_watched && !root_certificate.has_value()) {
157 root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
158 "Unable to get latest root certificates.");
160 if (identity_being_watched && !pem_key_cert_pairs.has_value()) {
161 identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
162 "Unable to get latest identity certificates.");
164 if (root_cert_error != GRPC_ERROR_NONE ||
165 identity_cert_error != GRPC_ERROR_NONE) {
166 distributor_->SetErrorForCert(cert_name, root_cert_error,
167 identity_cert_error);
172 FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
173 // Reset distributor's callback to make sure the callback won't be invoked
174 // again after this object(provider) is destroyed.
175 distributor_->SetWatchStatusCallback(nullptr);
176 gpr_event_set(&shutdown_event_, reinterpret_cast<void*>(1));
177 refresh_thread_.Join();
180 void FileWatcherCertificateProvider::ForceUpdate() {
181 absl::optional<std::string> root_certificate;
182 absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
183 if (!root_cert_path_.empty()) {
184 root_certificate = ReadRootCertificatesFromFile(root_cert_path_);
186 if (!private_key_path_.empty()) {
187 pem_key_cert_pairs = ReadIdentityKeyCertPairFromFiles(
188 private_key_path_, identity_certificate_path_);
190 grpc_core::MutexLock lock(&mu_);
191 const bool root_cert_changed =
192 (!root_certificate.has_value() && !root_certificate_.empty()) ||
193 (root_certificate.has_value() && root_certificate_ != *root_certificate);
194 if (root_cert_changed) {
195 if (root_certificate.has_value()) {
196 root_certificate_ = std::move(*root_certificate);
198 root_certificate_ = "";
201 const bool identity_cert_changed =
202 (!pem_key_cert_pairs.has_value() && !pem_key_cert_pairs_.empty()) ||
203 (pem_key_cert_pairs.has_value() &&
204 pem_key_cert_pairs_ != *pem_key_cert_pairs);
205 if (identity_cert_changed) {
206 if (pem_key_cert_pairs.has_value()) {
207 pem_key_cert_pairs_ = std::move(*pem_key_cert_pairs);
209 pem_key_cert_pairs_ = {};
212 if (root_cert_changed || identity_cert_changed) {
214 grpc_error_handle root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
215 "Unable to get latest root certificates.");
216 grpc_error_handle identity_cert_error =
217 GRPC_ERROR_CREATE_FROM_STATIC_STRING(
218 "Unable to get latest identity certificates.");
219 for (const auto& p : watcher_info_) {
220 const std::string& cert_name = p.first;
221 const WatcherInfo& info = p.second;
222 absl::optional<std::string> root_to_report;
223 absl::optional<grpc_core::PemKeyCertPairList> identity_to_report;
224 // Set key materials to the distributor if their contents changed.
225 if (info.root_being_watched && !root_certificate_.empty() &&
227 root_to_report = root_certificate_;
229 if (info.identity_being_watched && !pem_key_cert_pairs_.empty() &&
230 identity_cert_changed) {
231 identity_to_report = pem_key_cert_pairs_;
233 if (root_to_report.has_value() || identity_to_report.has_value()) {
234 distributor_->SetKeyMaterials(cert_name, std::move(root_to_report),
235 std::move(identity_to_report));
237 // Report errors to the distributor if the contents are empty.
238 const bool report_root_error =
239 info.root_being_watched && root_certificate_.empty();
240 const bool report_identity_error =
241 info.identity_being_watched && pem_key_cert_pairs_.empty();
242 if (report_root_error || report_identity_error) {
243 distributor_->SetErrorForCert(
245 report_root_error ? GRPC_ERROR_REF(root_cert_error)
247 report_identity_error ? GRPC_ERROR_REF(identity_cert_error)
251 GRPC_ERROR_UNREF(root_cert_error);
252 GRPC_ERROR_UNREF(identity_cert_error);
256 absl::optional<std::string>
257 FileWatcherCertificateProvider::ReadRootCertificatesFromFile(
258 const std::string& root_cert_full_path) {
259 // Read the root file.
260 grpc_slice root_slice = grpc_empty_slice();
261 grpc_error_handle root_error =
262 grpc_load_file(root_cert_full_path.c_str(), 0, &root_slice);
263 if (root_error != GRPC_ERROR_NONE) {
264 gpr_log(GPR_ERROR, "Reading file %s failed: %s",
265 root_cert_full_path.c_str(),
266 grpc_error_std_string(root_error).c_str());
267 GRPC_ERROR_UNREF(root_error);
268 return absl::nullopt;
270 std::string root_cert(StringViewFromSlice(root_slice));
271 grpc_slice_unref_internal(root_slice);
277 // This helper function gets the last-modified time of |filename|. When failed,
278 // it logs the error and returns 0.
279 time_t GetModificationTime(const char* filename) {
281 absl::Status status = grpc_core::GetFileModificationTime(filename, &ts);
287 absl::optional<PemKeyCertPairList>
288 FileWatcherCertificateProvider::ReadIdentityKeyCertPairFromFiles(
289 const std::string& private_key_path,
290 const std::string& identity_certificate_path) {
291 struct SliceWrapper {
292 grpc_slice slice = grpc_empty_slice();
293 ~SliceWrapper() { grpc_slice_unref_internal(slice); }
295 const int kNumRetryAttempts = 3;
296 for (int i = 0; i < kNumRetryAttempts; ++i) {
297 // TODO(ZhenLian): replace the timestamp approach with key-match approach
298 // once the latter is implemented.
299 // Checking the last modification of identity files before reading.
300 time_t identity_key_ts_before =
301 GetModificationTime(private_key_path.c_str());
302 if (identity_key_ts_before == 0) {
305 "Failed to get the file's modification time of %s. Start retrying...",
306 private_key_path.c_str());
309 time_t identity_cert_ts_before =
310 GetModificationTime(identity_certificate_path.c_str());
311 if (identity_cert_ts_before == 0) {
314 "Failed to get the file's modification time of %s. Start retrying...",
315 identity_certificate_path.c_str());
318 // Read the identity files.
319 SliceWrapper key_slice, cert_slice;
320 grpc_error_handle key_error =
321 grpc_load_file(private_key_path.c_str(), 0, &key_slice.slice);
322 if (key_error != GRPC_ERROR_NONE) {
323 gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
324 private_key_path.c_str(),
325 grpc_error_std_string(key_error).c_str());
326 GRPC_ERROR_UNREF(key_error);
329 grpc_error_handle cert_error =
330 grpc_load_file(identity_certificate_path.c_str(), 0, &cert_slice.slice);
331 if (cert_error != GRPC_ERROR_NONE) {
332 gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
333 identity_certificate_path.c_str(),
334 grpc_error_std_string(cert_error).c_str());
335 GRPC_ERROR_UNREF(cert_error);
338 std::string private_key(StringViewFromSlice(key_slice.slice));
339 std::string cert_chain(StringViewFromSlice(cert_slice.slice));
340 PemKeyCertPairList identity_pairs;
341 identity_pairs.emplace_back(private_key, cert_chain);
342 // Checking the last modification of identity files before reading.
343 time_t identity_key_ts_after =
344 GetModificationTime(private_key_path.c_str());
345 if (identity_key_ts_before != identity_key_ts_after) {
347 "Last modified time before and after reading %s is not the same. "
349 private_key_path.c_str());
352 time_t identity_cert_ts_after =
353 GetModificationTime(identity_certificate_path.c_str());
354 if (identity_cert_ts_before != identity_cert_ts_after) {
356 "Last modified time before and after reading %s is not the same. "
358 identity_certificate_path.c_str());
361 return identity_pairs;
364 "All retry attempts failed. Will try again after the next interval.");
365 return absl::nullopt;
368 absl::StatusOr<bool> PrivateKeyAndCertificateMatch(
369 absl::string_view private_key, absl::string_view cert_chain) {
370 if (private_key.empty()) {
371 return absl::InvalidArgumentError("Private key string is empty.");
373 if (cert_chain.empty()) {
374 return absl::InvalidArgumentError("Certificate string is empty.");
376 BIO* cert_bio = BIO_new_mem_buf(cert_chain.data(), cert_chain.size());
377 if (cert_bio == nullptr) {
378 return absl::InvalidArgumentError(
379 "Conversion from certificate string to BIO failed.");
381 // Reads the first cert from the cert_chain which is expected to be the leaf
383 X509* x509 = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
385 if (x509 == nullptr) {
386 return absl::InvalidArgumentError(
387 "Conversion from PEM string to X509 failed.");
389 EVP_PKEY* public_evp_pkey = X509_get_pubkey(x509);
391 if (public_evp_pkey == nullptr) {
392 return absl::InvalidArgumentError(
393 "Extraction of public key from x.509 certificate failed.");
395 BIO* private_key_bio =
396 BIO_new_mem_buf(private_key.data(), private_key.size());
397 if (private_key_bio == nullptr) {
398 EVP_PKEY_free(public_evp_pkey);
399 return absl::InvalidArgumentError(
400 "Conversion from private key string to BIO failed.");
402 EVP_PKEY* private_evp_pkey =
403 PEM_read_bio_PrivateKey(private_key_bio, nullptr, nullptr, nullptr);
404 BIO_free(private_key_bio);
405 if (private_evp_pkey == nullptr) {
406 EVP_PKEY_free(public_evp_pkey);
407 return absl::InvalidArgumentError(
408 "Conversion from PEM string to EVP_PKEY failed.");
410 bool result = EVP_PKEY_cmp(private_evp_pkey, public_evp_pkey) == 1;
411 EVP_PKEY_free(private_evp_pkey);
412 EVP_PKEY_free(public_evp_pkey);
416 } // namespace grpc_core
418 /** -- Wrapper APIs declared in grpc_security.h -- **/
420 grpc_tls_certificate_provider* grpc_tls_certificate_provider_static_data_create(
421 const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs) {
422 GPR_ASSERT(root_certificate != nullptr || pem_key_cert_pairs != nullptr);
423 grpc_core::ExecCtx exec_ctx;
424 grpc_core::PemKeyCertPairList identity_pairs_core;
425 if (pem_key_cert_pairs != nullptr) {
426 identity_pairs_core = std::move(pem_key_cert_pairs->pem_key_cert_pairs);
427 delete pem_key_cert_pairs;
429 std::string root_cert_core;
430 if (root_certificate != nullptr) {
431 root_cert_core = root_certificate;
433 return new grpc_core::StaticDataCertificateProvider(
434 std::move(root_cert_core), std::move(identity_pairs_core));
437 grpc_tls_certificate_provider*
438 grpc_tls_certificate_provider_file_watcher_create(
439 const char* private_key_path, const char* identity_certificate_path,
440 const char* root_cert_path, unsigned int refresh_interval_sec) {
441 grpc_core::ExecCtx exec_ctx;
442 return new grpc_core::FileWatcherCertificateProvider(
443 private_key_path == nullptr ? "" : private_key_path,
444 identity_certificate_path == nullptr ? "" : identity_certificate_path,
445 root_cert_path == nullptr ? "" : root_cert_path, refresh_interval_sec);
448 void grpc_tls_certificate_provider_release(
449 grpc_tls_certificate_provider* provider) {
450 GRPC_API_TRACE("grpc_tls_certificate_provider_release(provider=%p)", 1,
452 grpc_core::ExecCtx exec_ctx;
453 if (provider != nullptr) provider->Unref();