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