@@ -52,7 +52,7 @@ static const char* const root_certs[] = { |
52 | 52 |
|
53 | 53 | static const char system_cert_path[] = NODE_OPENSSL_SYSTEM_CERT_PATH; |
54 | 54 |
|
55 | | -static bool extra_root_certs_loaded = false; |
| 55 | +static std::string extra_root_certs_file; // NOLINT(runtime/string) |
56 | 56 |
|
57 | 57 | X509_STORE* GetOrCreateRootCertStore() { |
58 | 58 | // Guaranteed thread-safe by standard, just don't use -fno-threadsafe-statics. |
@@ -197,26 +197,66 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, |
197 | 197 | issuer); |
198 | 198 | } |
199 | 199 |
|
| 200 | +unsigned long LoadCertsFromFile( // NOLINT(runtime/int) |
| 201 | + std::vector<X509*>* certs, |
| 202 | + const char* file) { |
| 203 | + MarkPopErrorOnReturn mark_pop_error_on_return; |
| 204 | + |
| 205 | + BIOPointer bio(BIO_new_file(file, "r")); |
| 206 | + if (!bio) return ERR_get_error(); |
| 207 | + |
| 208 | + while (X509* x509 = PEM_read_bio_X509( |
| 209 | + bio.get(), nullptr, NoPasswordCallback, nullptr)) { |
| 210 | + certs->push_back(x509); |
| 211 | + } |
| 212 | + |
| 213 | + unsigned long err = ERR_peek_last_error(); // NOLINT(runtime/int) |
| 214 | + // Ignore error if its EOF/no start line found. |
| 215 | + if (ERR_GET_LIB(err) == ERR_LIB_PEM && |
| 216 | + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { |
| 217 | + return 0; |
| 218 | + } else { |
| 219 | + return err; |
| 220 | + } |
| 221 | +} |
| 222 | + |
200 | 223 | X509_STORE* NewRootCertStore() { |
201 | 224 | static std::vector<X509*> root_certs_vector; |
| 225 | + static bool root_certs_vector_loaded = false; |
202 | 226 | static Mutex root_certs_vector_mutex; |
203 | 227 | Mutex::ScopedLock lock(root_certs_vector_mutex); |
204 | 228 |
|
205 | | - if (root_certs_vector.empty() && |
206 | | - per_process::cli_options->ssl_openssl_cert_store == false) { |
207 | | - for (size_t i = 0; i < arraysize(root_certs); i++) { |
208 | | - X509* x509 = |
209 | | - PEM_read_bio_X509(NodeBIO::NewFixed(root_certs[i], |
210 | | - strlen(root_certs[i])).get(), |
211 | | - nullptr, // no re-use of X509 structure |
212 | | - NoPasswordCallback, |
213 | | - nullptr); // no callback data |
| 229 | + if (!root_certs_vector_loaded) { |
| 230 | + if (per_process::cli_options->ssl_openssl_cert_store == false) { |
| 231 | + for (size_t i = 0; i < arraysize(root_certs); i++) { |
| 232 | + X509* x509 = PEM_read_bio_X509( |
| 233 | + NodeBIO::NewFixed(root_certs[i], strlen(root_certs[i])).get(), |
| 234 | + nullptr, // no re-use of X509 structure |
| 235 | + NoPasswordCallback, |
| 236 | + nullptr); // no callback data |
| 237 | + |
| 238 | + // Parse errors from the built-in roots are fatal. |
| 239 | + CHECK_NOT_NULL(x509); |
214 | 240 |
|
215 | | - // Parse errors from the built-in roots are fatal. |
216 | | - CHECK_NOT_NULL(x509); |
| 241 | + root_certs_vector.push_back(x509); |
| 242 | + } |
| 243 | + } |
217 | 244 |
|
218 | | - root_certs_vector.push_back(x509); |
| 245 | + if (!extra_root_certs_file.empty()) { |
| 246 | + unsigned long err = LoadCertsFromFile( // NOLINT(runtime/int) |
| 247 | + &root_certs_vector, |
| 248 | + extra_root_certs_file.c_str()); |
| 249 | + if (err) { |
| 250 | + char buf[256]; |
| 251 | + ERR_error_string_n(err, buf, sizeof(buf)); |
| 252 | + fprintf(stderr, |
| 253 | + "Warning: Ignoring extra certs from `%s`, load failed: %s\n", |
| 254 | + extra_root_certs_file.c_str(), |
| 255 | + buf); |
| 256 | + } |
219 | 257 | } |
| 258 | + |
| 259 | + root_certs_vector_loaded = true; |
220 | 260 | } |
221 | 261 |
|
222 | 262 | X509_STORE* store = X509_STORE_new(); |
@@ -228,12 +268,11 @@ X509_STORE* NewRootCertStore() { |
228 | 268 |
|
229 | 269 | Mutex::ScopedLock cli_lock(node::per_process::cli_options_mutex); |
230 | 270 | if (per_process::cli_options->ssl_openssl_cert_store) { |
231 | | - X509_STORE_set_default_paths(store); |
232 | | - } else { |
233 | | - for (X509* cert : root_certs_vector) { |
234 | | - X509_up_ref(cert); |
235 | | - X509_STORE_add_cert(store, cert); |
236 | | - } |
| 271 | + CHECK_EQ(1, X509_STORE_set_default_paths(store)); |
| 272 | + } |
| 273 | + |
| 274 | + for (X509* cert : root_certs_vector) { |
| 275 | + CHECK_EQ(1, X509_STORE_add_cert(store, cert)); |
237 | 276 | } |
238 | 277 |
|
239 | 278 | return store; |
@@ -339,11 +378,6 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) { |
339 | 378 |
|
340 | 379 | SetMethodNoSideEffect( |
341 | 380 | context, target, "getRootCertificates", GetRootCertificates); |
342 | | - // Exposed for testing purposes only. |
343 | | - SetMethodNoSideEffect(context, |
344 | | - target, |
345 | | - "isExtraRootCertsFileLoaded", |
346 | | - IsExtraRootCertsFileLoaded); |
347 | 381 | } |
348 | 382 |
|
349 | 383 | void SecureContext::RegisterExternalReferences( |
@@ -383,7 +417,6 @@ void SecureContext::RegisterExternalReferences( |
383 | 417 | registry->Register(CtxGetter); |
384 | 418 |
|
385 | 419 | registry->Register(GetRootCertificates); |
386 | | - registry->Register(IsExtraRootCertsFileLoaded); |
387 | 420 | } |
388 | 421 |
|
389 | 422 | SecureContext* SecureContext::Create(Environment* env) { |
@@ -1383,54 +1416,9 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo<Value>& args) { |
1383 | 1416 | args.GetReturnValue().Set(buff); |
1384 | 1417 | } |
1385 | 1418 |
|
1386 | | -namespace { |
1387 | | -unsigned long AddCertsFromFile( // NOLINT(runtime/int) |
1388 | | - X509_STORE* store, |
1389 | | - const char* file) { |
1390 | | - ERR_clear_error(); |
1391 | | - MarkPopErrorOnReturn mark_pop_error_on_return; |
1392 | | - |
1393 | | - BIOPointer bio(BIO_new_file(file, "r")); |
1394 | | - if (!bio) |
1395 | | - return ERR_get_error(); |
1396 | | - |
1397 | | - while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509( |
1398 | | - bio.get(), nullptr, NoPasswordCallback, nullptr))) { |
1399 | | - X509_STORE_add_cert(store, x509.get()); |
1400 | | - } |
1401 | | - |
1402 | | - unsigned long err = ERR_peek_error(); // NOLINT(runtime/int) |
1403 | | - // Ignore error if its EOF/no start line found. |
1404 | | - if (ERR_GET_LIB(err) == ERR_LIB_PEM && |
1405 | | - ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { |
1406 | | - return 0; |
1407 | | - } |
1408 | | - |
1409 | | - return err; |
1410 | | -} |
1411 | | -} // namespace |
1412 | | - |
1413 | 1419 | // UseExtraCaCerts is called only once at the start of the Node.js process. |
1414 | 1420 | void UseExtraCaCerts(const std::string& file) { |
1415 | | - if (file.empty()) return; |
1416 | | - ClearErrorOnReturn clear_error_on_return; |
1417 | | - X509_STORE* store = GetOrCreateRootCertStore(); |
1418 | | - if (auto err = AddCertsFromFile(store, file.c_str())) { |
1419 | | - char buf[256]; |
1420 | | - ERR_error_string_n(err, buf, sizeof(buf)); |
1421 | | - fprintf(stderr, |
1422 | | - "Warning: Ignoring extra certs from `%s`, load failed: %s\n", |
1423 | | - file.c_str(), |
1424 | | - buf); |
1425 | | - } else { |
1426 | | - extra_root_certs_loaded = true; |
1427 | | - } |
1428 | | -} |
1429 | | - |
1430 | | -// Exposed to JavaScript strictly for testing purposes. |
1431 | | -void IsExtraRootCertsFileLoaded( |
1432 | | - const FunctionCallbackInfo<Value>& args) { |
1433 | | - return args.GetReturnValue().Set(extra_root_certs_loaded); |
| 1421 | + extra_root_certs_file = file; |
1434 | 1422 | } |
1435 | 1423 |
|
1436 | 1424 | } // namespace crypto |
|