Imported Upstream version 1.40.0
[platform/upstream/grpc.git] / src / core / lib / security / credentials / tls / grpc_tls_certificate_provider.cc
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 #include <openssl/ssl.h>
25
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"
29
30 namespace grpc_core {
31
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_;
48     }
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_;
53     }
54     info.identity_being_watched = identity_being_watched;
55     if (!info.root_being_watched && !info.identity_being_watched) {
56       watcher_info_.erase(cert_name);
57     }
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));
63     }
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.");
69     }
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.");
73     }
74     if (root_cert_error != GRPC_ERROR_NONE ||
75         identity_cert_error != GRPC_ERROR_NONE) {
76       distributor_->SetErrorForCert(cert_name, root_cert_error,
77                                     identity_cert_error);
78     }
79   });
80 }
81
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);
86 }
87
88 namespace {
89
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));
93 }
94
95 }  // namespace
96
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_);
110   ForceUpdate();
111   auto thread_lambda = [](void* arg) {
112     FileWatcherCertificateProvider* provider =
113         static_cast<FileWatcherCertificateProvider*>(arg);
114     GPR_ASSERT(provider != nullptr);
115     while (true) {
116       void* value = gpr_event_wait(
117           &provider->shutdown_event_,
118           TimeoutSecondsToDeadline(provider->refresh_interval_sec_));
119       if (value != nullptr) {
120         return;
121       };
122       provider->ForceUpdate();
123     }
124   };
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_;
139     }
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_;
144     }
145     info.identity_being_watched = identity_being_watched;
146     if (!info.root_being_watched && !info.identity_being_watched) {
147       watcher_info_.erase(cert_name);
148     }
149     ExecCtx exec_ctx;
150     if (root_certificate.has_value() || pem_key_cert_pairs.has_value()) {
151       distributor_->SetKeyMaterials(cert_name, root_certificate,
152                                     pem_key_cert_pairs);
153     }
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.");
159     }
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.");
163     }
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);
168     }
169   });
170 }
171
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();
178 }
179
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_);
185   }
186   if (!private_key_path_.empty()) {
187     pem_key_cert_pairs = ReadIdentityKeyCertPairFromFiles(
188         private_key_path_, identity_certificate_path_);
189   }
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);
197     } else {
198       root_certificate_ = "";
199     }
200   }
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);
208     } else {
209       pem_key_cert_pairs_ = {};
210     }
211   }
212   if (root_cert_changed || identity_cert_changed) {
213     ExecCtx exec_ctx;
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() &&
226           root_cert_changed) {
227         root_to_report = root_certificate_;
228       }
229       if (info.identity_being_watched && !pem_key_cert_pairs_.empty() &&
230           identity_cert_changed) {
231         identity_to_report = pem_key_cert_pairs_;
232       }
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));
236       }
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(
244             cert_name,
245             report_root_error ? GRPC_ERROR_REF(root_cert_error)
246                               : GRPC_ERROR_NONE,
247             report_identity_error ? GRPC_ERROR_REF(identity_cert_error)
248                                   : GRPC_ERROR_NONE);
249       }
250     }
251     GRPC_ERROR_UNREF(root_cert_error);
252     GRPC_ERROR_UNREF(identity_cert_error);
253   }
254 }
255
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;
269   }
270   std::string root_cert(StringViewFromSlice(root_slice));
271   grpc_slice_unref_internal(root_slice);
272   return root_cert;
273 }
274
275 namespace {
276
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) {
280   time_t ts = 0;
281   absl::Status status = grpc_core::GetFileModificationTime(filename, &ts);
282   return ts;
283 }
284
285 }  // namespace
286
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); }
294   };
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) {
303       gpr_log(
304           GPR_ERROR,
305           "Failed to get the file's modification time of %s. Start retrying...",
306           private_key_path.c_str());
307       continue;
308     }
309     time_t identity_cert_ts_before =
310         GetModificationTime(identity_certificate_path.c_str());
311     if (identity_cert_ts_before == 0) {
312       gpr_log(
313           GPR_ERROR,
314           "Failed to get the file's modification time of %s. Start retrying...",
315           identity_certificate_path.c_str());
316       continue;
317     }
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);
327       continue;
328     }
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);
336       continue;
337     }
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) {
346       gpr_log(GPR_ERROR,
347               "Last modified time before and after reading %s is not the same. "
348               "Start retrying...",
349               private_key_path.c_str());
350       continue;
351     }
352     time_t identity_cert_ts_after =
353         GetModificationTime(identity_certificate_path.c_str());
354     if (identity_cert_ts_before != identity_cert_ts_after) {
355       gpr_log(GPR_ERROR,
356               "Last modified time before and after reading %s is not the same. "
357               "Start retrying...",
358               identity_certificate_path.c_str());
359       continue;
360     }
361     return identity_pairs;
362   }
363   gpr_log(GPR_ERROR,
364           "All retry attempts failed. Will try again after the next interval.");
365   return absl::nullopt;
366 }
367
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.");
372   }
373   if (cert_chain.empty()) {
374     return absl::InvalidArgumentError("Certificate string is empty.");
375   }
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.");
380   }
381   // Reads the first cert from the cert_chain which is expected to be the leaf
382   // cert
383   X509* x509 = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
384   BIO_free(cert_bio);
385   if (x509 == nullptr) {
386     return absl::InvalidArgumentError(
387         "Conversion from PEM string to X509 failed.");
388   }
389   EVP_PKEY* public_evp_pkey = X509_get_pubkey(x509);
390   X509_free(x509);
391   if (public_evp_pkey == nullptr) {
392     return absl::InvalidArgumentError(
393         "Extraction of public key from x.509 certificate failed.");
394   }
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.");
401   }
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.");
409   }
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);
413   return result;
414 }
415
416 }  // namespace grpc_core
417
418 /** -- Wrapper APIs declared in grpc_security.h -- **/
419
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;
428   }
429   std::string root_cert_core;
430   if (root_certificate != nullptr) {
431     root_cert_core = root_certificate;
432   }
433   return new grpc_core::StaticDataCertificateProvider(
434       std::move(root_cert_core), std::move(identity_pairs_core));
435 }
436
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);
446 }
447
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,
451                  (provider));
452   grpc_core::ExecCtx exec_ctx;
453   if (provider != nullptr) provider->Unref();
454 }