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