• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2018 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include <algorithm>
22 #include <string>
23 #include <vector>
24 
25 #if defined(GPR_LINUX) || defined(GPR_ANDROID) || defined(GPR_FREEBSD) || \
26     defined(GPR_APPLE)
27 
28 #include <dirent.h>
29 #include <fcntl.h>
30 #include <grpc/support/alloc.h>
31 #include <stdio.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 
36 #include "absl/log/log.h"
37 #include "src/core/config/config_vars.h"
38 #include "src/core/lib/iomgr/error.h"
39 #include "src/core/lib/security/security_connector/load_system_roots.h"
40 #include "src/core/lib/security/security_connector/load_system_roots_supported.h"
41 #include "src/core/util/load_file.h"
42 #include "src/core/util/useful.h"
43 
44 namespace grpc_core {
45 namespace {
46 
47 #if defined(GPR_LINUX) || defined(GPR_ANDROID)
48 const char* kCertFiles[] = {
49     "/etc/ssl/certs/ca-certificates.crt", "/etc/pki/tls/certs/ca-bundle.crt",
50     "/etc/ssl/ca-bundle.pem", "/etc/pki/tls/cacert.pem",
51     "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"};
52 const char* kCertDirectories[] = {
53     "/etc/ssl/certs", "/system/etc/security/cacerts", "/usr/local/share/certs",
54     "/etc/pki/tls/certs", "/etc/openssl/certs"};
55 #elif defined(GPR_FREEBSD)  // endif GPR_LINUX || GPR_ANDROID
56 const char* kCertFiles[] = {"/etc/ssl/cert.pem",
57                             "/usr/local/share/certs/ca-root-nss.crt"};
58 const char* kCertDirectories[] = {""};
59 #elif defined(GPR_APPLE)    // endif GPR_FREEBSD
60 const char* kCertFiles[] = {"/etc/ssl/cert.pem"};
61 const char* kCertDirectories[] = {""};
62 #endif                      // GPR_APPLE
63 
GetSystemRootCerts()64 grpc_slice GetSystemRootCerts() {
65   size_t num_cert_files_ = GPR_ARRAY_SIZE(kCertFiles);
66   for (size_t i = 0; i < num_cert_files_; i++) {
67     auto slice = LoadFile(kCertFiles[i], /*add_null_terminator=*/true);
68     if (slice.ok()) return slice->TakeCSlice();
69   }
70   return grpc_empty_slice();
71 }
72 
73 }  // namespace
74 
GetAbsoluteFilePath(const char * valid_file_dir,const char * file_entry_name,char * path_buffer)75 void GetAbsoluteFilePath(const char* valid_file_dir,
76                          const char* file_entry_name, char* path_buffer) {
77   if (valid_file_dir != nullptr && file_entry_name != nullptr) {
78     int path_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", valid_file_dir,
79                             file_entry_name);
80     if (path_len == 0) {
81       LOG(ERROR) << "failed to get absolute path for file: " << file_entry_name;
82     }
83   }
84 }
85 
CreateRootCertsBundle(const char * certs_directory)86 grpc_slice CreateRootCertsBundle(const char* certs_directory) {
87   grpc_slice bundle_slice = grpc_empty_slice();
88   if (certs_directory == nullptr) {
89     return bundle_slice;
90   }
91   DIR* ca_directory = opendir(certs_directory);
92   if (ca_directory == nullptr) {
93     return bundle_slice;
94   }
95   struct FileData {
96     char path[MAXPATHLEN];
97     off_t size;
98   };
99   std::vector<FileData> roots_filenames;
100   size_t total_bundle_size = 0;
101   struct dirent* directory_entry;
102   while ((directory_entry = readdir(ca_directory)) != nullptr) {
103     struct stat dir_entry_stat;
104     const char* file_entry_name = directory_entry->d_name;
105     FileData file_data;
106     GetAbsoluteFilePath(certs_directory, file_entry_name, file_data.path);
107     int stat_return = stat(file_data.path, &dir_entry_stat);
108     if (stat_return == -1 || !S_ISREG(dir_entry_stat.st_mode)) {
109       // no subdirectories.
110       if (stat_return == -1) {
111         LOG(ERROR) << "failed to get status for file: " << file_data.path;
112       }
113       continue;
114     }
115     file_data.size = dir_entry_stat.st_size;
116     total_bundle_size += file_data.size;
117     roots_filenames.push_back(file_data);
118   }
119   closedir(ca_directory);
120   char* bundle_string = static_cast<char*>(gpr_zalloc(total_bundle_size + 1));
121   size_t bytes_read = 0;
122   for (size_t i = 0; i < roots_filenames.size(); i++) {
123     int file_descriptor = open(roots_filenames[i].path, O_RDONLY);
124     if (file_descriptor != -1) {
125       // Read file into bundle.
126       size_t cert_file_size = roots_filenames[i].size;
127       int read_ret =
128           read(file_descriptor, bundle_string + bytes_read, cert_file_size);
129       if (read_ret != -1) {
130         bytes_read += read_ret;
131       } else {
132         LOG(ERROR) << "failed to read file: " << roots_filenames[i].path;
133       }
134     }
135   }
136   bundle_slice = grpc_slice_new(bundle_string, bytes_read, gpr_free);
137   return bundle_slice;
138 }
139 
LoadSystemRootCerts()140 grpc_slice LoadSystemRootCerts() {
141   grpc_slice result = grpc_empty_slice();
142   // Prioritize user-specified custom directory if flag is set.
143   auto custom_dir = ConfigVars::Get().SystemSslRootsDir();
144   if (!custom_dir.empty()) {
145     result = CreateRootCertsBundle(std::string(custom_dir).c_str());
146   }
147   // If the custom directory is empty/invalid/not specified, fallback to
148   // distribution-specific directory.
149   if (GRPC_SLICE_IS_EMPTY(result)) {
150     result = GetSystemRootCerts();
151   }
152   if (GRPC_SLICE_IS_EMPTY(result)) {
153     for (size_t i = 0; i < GPR_ARRAY_SIZE(kCertDirectories); i++) {
154       result = CreateRootCertsBundle(kCertDirectories[i]);
155       if (!GRPC_SLICE_IS_EMPTY(result)) {
156         break;
157       }
158     }
159   }
160   return result;
161 }
162 
163 }  // namespace grpc_core
164 
165 #endif  // GPR_LINUX || GPR_ANDROID || GPR_FREEBSD || GPR_APPLE
166