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