1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/core/tsi/ssl/key_logging/ssl_key_logging.h"
16
17 #include <grpc/support/port_platform.h>
18
19 #include <map>
20
21 #include "absl/log/check.h"
22 #include "absl/log/log.h"
23 #include "src/core/lib/iomgr/error.h"
24 #include "src/core/lib/slice/slice_internal.h"
25 #include "src/core/util/crash.h"
26 #include "src/core/util/sync.h"
27
28 using TlsSessionKeyLogger = tsi::TlsSessionKeyLoggerCache::TlsSessionKeyLogger;
29
30 namespace tsi {
31
32 namespace {
33
34 gpr_once g_cache_mutex_init = GPR_ONCE_INIT;
35 grpc_core::Mutex* g_tls_session_key_log_cache_mu = nullptr;
36 // A pointer to a global singleton instance.
37 TlsSessionKeyLoggerCache* g_cache_instance
38 ABSL_GUARDED_BY(g_tls_session_key_log_cache_mu) = nullptr;
39
do_cache_mutex_init(void)40 void do_cache_mutex_init(void) {
41 g_tls_session_key_log_cache_mu = new grpc_core::Mutex();
42 }
43
44 } // namespace
45
TlsSessionKeyLogger(std::string tls_session_key_log_file_path,grpc_core::RefCountedPtr<TlsSessionKeyLoggerCache> cache)46 TlsSessionKeyLoggerCache::TlsSessionKeyLogger::TlsSessionKeyLogger(
47 std::string tls_session_key_log_file_path,
48 grpc_core::RefCountedPtr<TlsSessionKeyLoggerCache> cache)
49 : tls_session_key_log_file_path_(std::move(tls_session_key_log_file_path)),
50 cache_(std::move(cache)) {
51 CHECK(!tls_session_key_log_file_path_.empty());
52 CHECK(cache_ != nullptr);
53 fd_ = fopen(tls_session_key_log_file_path_.c_str(), "a");
54 if (fd_ == nullptr) {
55 grpc_error_handle error = GRPC_OS_ERROR(errno, "fopen");
56 LOG(ERROR) << "Ignoring TLS Key logging. ERROR Opening TLS Keylog file: "
57 << grpc_core::StatusToString(error);
58 }
59 cache_->tls_session_key_logger_map_.emplace(tls_session_key_log_file_path_,
60 this);
61 };
62
~TlsSessionKeyLogger()63 TlsSessionKeyLoggerCache::TlsSessionKeyLogger::~TlsSessionKeyLogger() {
64 {
65 grpc_core::MutexLock lock(&lock_);
66 if (fd_ != nullptr) fclose(fd_);
67 }
68 {
69 grpc_core::MutexLock lock(g_tls_session_key_log_cache_mu);
70 auto it = cache_->tls_session_key_logger_map_.find(
71 tls_session_key_log_file_path_);
72 if (it != cache_->tls_session_key_logger_map_.end() && it->second == this) {
73 cache_->tls_session_key_logger_map_.erase(it);
74 }
75 }
76 }
77
LogSessionKeys(SSL_CTX *,const std::string & session_keys_info)78 void TlsSessionKeyLoggerCache::TlsSessionKeyLogger::LogSessionKeys(
79 SSL_CTX* /* ssl_context */, const std::string& session_keys_info) {
80 grpc_core::MutexLock lock(&lock_);
81 if (fd_ == nullptr || session_keys_info.empty()) return;
82 // Append to key log file under lock
83 bool err =
84 fwrite((session_keys_info + "\n").c_str(), sizeof(char),
85 session_keys_info.length() + 1, fd_) < session_keys_info.length();
86
87 if (err) {
88 grpc_error_handle error = GRPC_OS_ERROR(errno, "fwrite");
89 LOG(ERROR) << "Error Appending to TLS session key log file: "
90 << grpc_core::StatusToString(error);
91 fclose(fd_);
92 fd_ = nullptr; // disable future attempts to write to this file
93 } else {
94 fflush(fd_);
95 }
96 }
97
TlsSessionKeyLoggerCache()98 TlsSessionKeyLoggerCache::TlsSessionKeyLoggerCache()
99 ABSL_EXCLUSIVE_LOCKS_REQUIRED(g_tls_session_key_log_cache_mu) {
100 g_cache_instance = this;
101 }
102
~TlsSessionKeyLoggerCache()103 TlsSessionKeyLoggerCache::~TlsSessionKeyLoggerCache() {
104 grpc_core::MutexLock lock(g_tls_session_key_log_cache_mu);
105 g_cache_instance = nullptr;
106 }
107
Get(std::string tls_session_key_log_file_path)108 grpc_core::RefCountedPtr<TlsSessionKeyLogger> TlsSessionKeyLoggerCache::Get(
109 std::string tls_session_key_log_file_path) {
110 gpr_once_init(&g_cache_mutex_init, do_cache_mutex_init);
111 DCHECK_NE(g_tls_session_key_log_cache_mu, nullptr);
112 if (tls_session_key_log_file_path.empty()) {
113 return nullptr;
114 }
115 {
116 grpc_core::MutexLock lock(g_tls_session_key_log_cache_mu);
117 grpc_core::RefCountedPtr<TlsSessionKeyLoggerCache> cache;
118 if (g_cache_instance == nullptr) {
119 // This will automatically set g_cache_instance.
120 // Not using MakeRefCounted because to the thread safety analysis, which
121 // cannot see through calls, it would look like MakeRefCounted was
122 // calling TlsSessionKeyLoggerCache without holding a lock on
123 // g_tls_session_key_log_cache_mu.
124 cache.reset(new TlsSessionKeyLoggerCache());
125 } else {
126 cache = g_cache_instance->Ref();
127 }
128 // Check cache for entry.
129 auto it =
130 cache->tls_session_key_logger_map_.find(tls_session_key_log_file_path);
131 if (it != cache->tls_session_key_logger_map_.end()) {
132 // Avoid a race condition if the destructor of the tls key logger
133 // of interest is currently executing.
134 auto key_logger = it->second->RefIfNonZero();
135 if (key_logger != nullptr) return key_logger;
136 }
137 // Not found in cache, so create new entry.
138 // This will automatically add itself to tls_session_key_logger_map_.
139 return grpc_core::MakeRefCounted<TlsSessionKeyLogger>(
140 std::move(tls_session_key_log_file_path), std::move(cache));
141 }
142 }
143
144 }; // namespace tsi
145