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