summaryrefslogtreecommitdiff
path: root/grpc/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'grpc/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc')
-rw-r--r--grpc/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc321
1 files changed, 321 insertions, 0 deletions
diff --git a/grpc/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc b/grpc/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc
new file mode 100644
index 00000000..2189865b
--- /dev/null
+++ b/grpc/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc
@@ -0,0 +1,321 @@
+//
+// Copyright 2020 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <stdlib.h>
+#include <string.h>
+
+void grpc_tls_certificate_distributor::SetKeyMaterials(
+ const std::string& cert_name, absl::optional<std::string> pem_root_certs,
+ absl::optional<PemKeyCertPairList> pem_key_cert_pairs) {
+ GPR_ASSERT(pem_root_certs.has_value() || pem_key_cert_pairs.has_value());
+ grpc_core::MutexLock lock(&mu_);
+ auto& cert_info = certificate_info_map_[cert_name];
+ if (pem_root_certs.has_value()) {
+ // Successful credential updates will clear any pre-existing error.
+ cert_info.SetRootError(GRPC_ERROR_NONE);
+ for (auto* watcher_ptr : cert_info.root_cert_watchers) {
+ GPR_ASSERT(watcher_ptr != nullptr);
+ const auto watcher_it = watchers_.find(watcher_ptr);
+ GPR_ASSERT(watcher_it != watchers_.end());
+ GPR_ASSERT(watcher_it->second.root_cert_name.has_value());
+ absl::optional<PemKeyCertPairList> pem_key_cert_pairs_to_report;
+ if (pem_key_cert_pairs.has_value() &&
+ watcher_it->second.identity_cert_name == cert_name) {
+ pem_key_cert_pairs_to_report = pem_key_cert_pairs;
+ } else if (watcher_it->second.identity_cert_name.has_value()) {
+ auto& identity_cert_info =
+ certificate_info_map_[*watcher_it->second.identity_cert_name];
+ pem_key_cert_pairs_to_report = identity_cert_info.pem_key_cert_pairs;
+ }
+ watcher_ptr->OnCertificatesChanged(
+ pem_root_certs, std::move(pem_key_cert_pairs_to_report));
+ }
+ cert_info.pem_root_certs = std::move(*pem_root_certs);
+ }
+ if (pem_key_cert_pairs.has_value()) {
+ // Successful credential updates will clear any pre-existing error.
+ cert_info.SetIdentityError(GRPC_ERROR_NONE);
+ for (const auto watcher_ptr : cert_info.identity_cert_watchers) {
+ GPR_ASSERT(watcher_ptr != nullptr);
+ const auto watcher_it = watchers_.find(watcher_ptr);
+ GPR_ASSERT(watcher_it != watchers_.end());
+ GPR_ASSERT(watcher_it->second.identity_cert_name.has_value());
+ absl::optional<absl::string_view> pem_root_certs_to_report;
+ if (pem_root_certs.has_value() &&
+ watcher_it->second.root_cert_name == cert_name) {
+ // In this case, We've already sent the credential updates at the time
+ // when checking pem_root_certs, so we will skip here.
+ continue;
+ } else if (watcher_it->second.root_cert_name.has_value()) {
+ auto& root_cert_info =
+ certificate_info_map_[*watcher_it->second.root_cert_name];
+ pem_root_certs_to_report = root_cert_info.pem_root_certs;
+ }
+ watcher_ptr->OnCertificatesChanged(pem_root_certs_to_report,
+ pem_key_cert_pairs);
+ }
+ cert_info.pem_key_cert_pairs = std::move(*pem_key_cert_pairs);
+ }
+}
+
+bool grpc_tls_certificate_distributor::HasRootCerts(
+ const std::string& root_cert_name) {
+ grpc_core::MutexLock lock(&mu_);
+ const auto it = certificate_info_map_.find(root_cert_name);
+ return it != certificate_info_map_.end() &&
+ !it->second.pem_root_certs.empty();
+};
+
+bool grpc_tls_certificate_distributor::HasKeyCertPairs(
+ const std::string& identity_cert_name) {
+ grpc_core::MutexLock lock(&mu_);
+ const auto it = certificate_info_map_.find(identity_cert_name);
+ return it != certificate_info_map_.end() &&
+ !it->second.pem_key_cert_pairs.empty();
+};
+
+void grpc_tls_certificate_distributor::SetErrorForCert(
+ const std::string& cert_name, absl::optional<grpc_error*> root_cert_error,
+ absl::optional<grpc_error*> identity_cert_error) {
+ GPR_ASSERT(root_cert_error.has_value() || identity_cert_error.has_value());
+ grpc_core::MutexLock lock(&mu_);
+ CertificateInfo& cert_info = certificate_info_map_[cert_name];
+ if (root_cert_error.has_value()) {
+ for (auto* watcher_ptr : cert_info.root_cert_watchers) {
+ GPR_ASSERT(watcher_ptr != nullptr);
+ const auto watcher_it = watchers_.find(watcher_ptr);
+ GPR_ASSERT(watcher_it != watchers_.end());
+ // identity_cert_error_to_report is the error of the identity cert this
+ // watcher is watching, if there is any.
+ grpc_error* identity_cert_error_to_report = GRPC_ERROR_NONE;
+ if (identity_cert_error.has_value() &&
+ watcher_it->second.identity_cert_name == cert_name) {
+ identity_cert_error_to_report = *identity_cert_error;
+ } else if (watcher_it->second.identity_cert_name.has_value()) {
+ auto& identity_cert_info =
+ certificate_info_map_[*watcher_it->second.identity_cert_name];
+ identity_cert_error_to_report = identity_cert_info.identity_cert_error;
+ }
+ watcher_ptr->OnError(GRPC_ERROR_REF(*root_cert_error),
+ GRPC_ERROR_REF(identity_cert_error_to_report));
+ }
+ cert_info.SetRootError(*root_cert_error);
+ }
+ if (identity_cert_error.has_value()) {
+ for (auto* watcher_ptr : cert_info.identity_cert_watchers) {
+ GPR_ASSERT(watcher_ptr != nullptr);
+ const auto watcher_it = watchers_.find(watcher_ptr);
+ GPR_ASSERT(watcher_it != watchers_.end());
+ // root_cert_error_to_report is the error of the root cert this watcher is
+ // watching, if there is any.
+ grpc_error* root_cert_error_to_report = GRPC_ERROR_NONE;
+ if (root_cert_error.has_value() &&
+ watcher_it->second.root_cert_name == cert_name) {
+ // In this case, We've already sent the error updates at the time when
+ // checking root_cert_error, so we will skip here.
+ continue;
+ } else if (watcher_it->second.root_cert_name.has_value()) {
+ auto& root_cert_info =
+ certificate_info_map_[*watcher_it->second.root_cert_name];
+ root_cert_error_to_report = root_cert_info.root_cert_error;
+ }
+ watcher_ptr->OnError(GRPC_ERROR_REF(root_cert_error_to_report),
+ GRPC_ERROR_REF(*identity_cert_error));
+ }
+ cert_info.SetIdentityError(*identity_cert_error);
+ }
+};
+
+void grpc_tls_certificate_distributor::SetError(grpc_error* error) {
+ GPR_ASSERT(error != GRPC_ERROR_NONE);
+ grpc_core::MutexLock lock(&mu_);
+ for (const auto& watcher : watchers_) {
+ const auto watcher_ptr = watcher.first;
+ GPR_ASSERT(watcher_ptr != nullptr);
+ const auto& watcher_info = watcher.second;
+ watcher_ptr->OnError(
+ watcher_info.root_cert_name.has_value() ? GRPC_ERROR_REF(error)
+ : GRPC_ERROR_NONE,
+ watcher_info.identity_cert_name.has_value() ? GRPC_ERROR_REF(error)
+ : GRPC_ERROR_NONE);
+ }
+ for (auto& cert_info_entry : certificate_info_map_) {
+ auto& cert_info = cert_info_entry.second;
+ cert_info.SetRootError(GRPC_ERROR_REF(error));
+ cert_info.SetIdentityError(GRPC_ERROR_REF(error));
+ }
+ GRPC_ERROR_UNREF(error);
+};
+
+void grpc_tls_certificate_distributor::WatchTlsCertificates(
+ std::unique_ptr<TlsCertificatesWatcherInterface> watcher,
+ absl::optional<std::string> root_cert_name,
+ absl::optional<std::string> identity_cert_name) {
+ bool start_watching_root_cert = false;
+ bool already_watching_identity_for_root_cert = false;
+ bool start_watching_identity_cert = false;
+ bool already_watching_root_for_identity_cert = false;
+ GPR_ASSERT(root_cert_name.has_value() || identity_cert_name.has_value());
+ TlsCertificatesWatcherInterface* watcher_ptr = watcher.get();
+ GPR_ASSERT(watcher_ptr != nullptr);
+ // Update watchers_ and certificate_info_map_.
+ {
+ grpc_core::MutexLock lock(&mu_);
+ const auto watcher_it = watchers_.find(watcher_ptr);
+ // The caller needs to cancel the watcher first if it wants to re-register
+ // the watcher.
+ GPR_ASSERT(watcher_it == watchers_.end());
+ watchers_[watcher_ptr] = {std::move(watcher), root_cert_name,
+ identity_cert_name};
+ absl::optional<absl::string_view> updated_root_certs;
+ absl::optional<PemKeyCertPairList> updated_identity_pairs;
+ grpc_error* root_error = GRPC_ERROR_NONE;
+ grpc_error* identity_error = GRPC_ERROR_NONE;
+ if (root_cert_name.has_value()) {
+ CertificateInfo& cert_info = certificate_info_map_[*root_cert_name];
+ start_watching_root_cert = cert_info.root_cert_watchers.empty();
+ already_watching_identity_for_root_cert =
+ !cert_info.identity_cert_watchers.empty();
+ cert_info.root_cert_watchers.insert(watcher_ptr);
+ root_error = GRPC_ERROR_REF(cert_info.root_cert_error);
+ // Empty credentials will be treated as no updates.
+ if (!cert_info.pem_root_certs.empty()) {
+ updated_root_certs = cert_info.pem_root_certs;
+ }
+ }
+ if (identity_cert_name.has_value()) {
+ CertificateInfo& cert_info = certificate_info_map_[*identity_cert_name];
+ start_watching_identity_cert = cert_info.identity_cert_watchers.empty();
+ already_watching_root_for_identity_cert =
+ !cert_info.root_cert_watchers.empty();
+ cert_info.identity_cert_watchers.insert(watcher_ptr);
+ identity_error = GRPC_ERROR_REF(cert_info.identity_cert_error);
+ // Empty credentials will be treated as no updates.
+ if (!cert_info.pem_key_cert_pairs.empty()) {
+ updated_identity_pairs = cert_info.pem_key_cert_pairs;
+ }
+ }
+ // Notify this watcher if the certs it is watching already had some
+ // contents. Note that an *_cert_error in cert_info only indicates error
+ // occurred while trying to fetch the latest cert, but the updated_*_certs
+ // should always be valid. So we will send the updates regardless of
+ // *_cert_error.
+ if (updated_root_certs.has_value() || updated_identity_pairs.has_value()) {
+ watcher_ptr->OnCertificatesChanged(updated_root_certs,
+ std::move(updated_identity_pairs));
+ }
+ // Notify this watcher if the certs it is watching already had some errors.
+ if (root_error != GRPC_ERROR_NONE || identity_error != GRPC_ERROR_NONE) {
+ watcher_ptr->OnError(GRPC_ERROR_REF(root_error),
+ GRPC_ERROR_REF(identity_error));
+ }
+ GRPC_ERROR_UNREF(root_error);
+ GRPC_ERROR_UNREF(identity_error);
+ }
+ // Invoke watch status callback if needed.
+ {
+ grpc_core::MutexLock lock(&callback_mu_);
+ if (watch_status_callback_ != nullptr) {
+ if (root_cert_name == identity_cert_name &&
+ (start_watching_root_cert || start_watching_identity_cert)) {
+ watch_status_callback_(*root_cert_name, start_watching_root_cert,
+ start_watching_identity_cert);
+ } else {
+ if (start_watching_root_cert) {
+ watch_status_callback_(*root_cert_name, true,
+ already_watching_identity_for_root_cert);
+ }
+ if (start_watching_identity_cert) {
+ watch_status_callback_(*identity_cert_name,
+ already_watching_root_for_identity_cert, true);
+ }
+ }
+ }
+ }
+};
+
+void grpc_tls_certificate_distributor::CancelTlsCertificatesWatch(
+ TlsCertificatesWatcherInterface* watcher) {
+ absl::optional<std::string> root_cert_name;
+ absl::optional<std::string> identity_cert_name;
+ bool stop_watching_root_cert = false;
+ bool already_watching_identity_for_root_cert = false;
+ bool stop_watching_identity_cert = false;
+ bool already_watching_root_for_identity_cert = false;
+ // Update watchers_ and certificate_info_map_.
+ {
+ grpc_core::MutexLock lock(&mu_);
+ auto it = watchers_.find(watcher);
+ if (it == watchers_.end()) return;
+ WatcherInfo& watcher_info = it->second;
+ root_cert_name = std::move(watcher_info.root_cert_name);
+ identity_cert_name = std::move(watcher_info.identity_cert_name);
+ watchers_.erase(it);
+ if (root_cert_name.has_value()) {
+ auto it = certificate_info_map_.find(*root_cert_name);
+ GPR_ASSERT(it != certificate_info_map_.end());
+ CertificateInfo& cert_info = it->second;
+ cert_info.root_cert_watchers.erase(watcher);
+ stop_watching_root_cert = cert_info.root_cert_watchers.empty();
+ already_watching_identity_for_root_cert =
+ !cert_info.identity_cert_watchers.empty();
+ if (stop_watching_root_cert && !already_watching_identity_for_root_cert) {
+ certificate_info_map_.erase(it);
+ }
+ }
+ if (identity_cert_name.has_value()) {
+ auto it = certificate_info_map_.find(*identity_cert_name);
+ GPR_ASSERT(it != certificate_info_map_.end());
+ CertificateInfo& cert_info = it->second;
+ cert_info.identity_cert_watchers.erase(watcher);
+ stop_watching_identity_cert = cert_info.identity_cert_watchers.empty();
+ already_watching_root_for_identity_cert =
+ !cert_info.root_cert_watchers.empty();
+ if (stop_watching_identity_cert &&
+ !already_watching_root_for_identity_cert) {
+ certificate_info_map_.erase(it);
+ }
+ }
+ }
+ // Invoke watch status callback if needed.
+ {
+ grpc_core::MutexLock lock(&callback_mu_);
+ if (watch_status_callback_ != nullptr) {
+ if (root_cert_name == identity_cert_name &&
+ (stop_watching_root_cert || stop_watching_identity_cert)) {
+ watch_status_callback_(*root_cert_name, !stop_watching_root_cert,
+ !stop_watching_identity_cert);
+ } else {
+ if (stop_watching_root_cert) {
+ watch_status_callback_(*root_cert_name, false,
+ already_watching_identity_for_root_cert);
+ }
+ if (stop_watching_identity_cert) {
+ watch_status_callback_(*identity_cert_name,
+ already_watching_root_for_identity_cert,
+ false);
+ }
+ }
+ }
+ }
+};