@@ -42,11 +42,13 @@ using ncrypto::MarkPopErrorOnReturn; |
42 | 42 | using ncrypto::SSLPointer; |
43 | 43 | using ncrypto::StackOfX509; |
44 | 44 | using ncrypto::X509Pointer; |
| 45 | +using ncrypto::X509View; |
45 | 46 | using v8::Array; |
46 | 47 | using v8::ArrayBufferView; |
47 | 48 | using v8::Boolean; |
48 | 49 | using v8::Context; |
49 | 50 | using v8::DontDelete; |
| 51 | +using v8::EscapableHandleScope; |
50 | 52 | using v8::Exception; |
51 | 53 | using v8::External; |
52 | 54 | using v8::FunctionCallbackInfo; |
@@ -57,7 +59,9 @@ using v8::Integer; |
57 | 59 | using v8::Isolate; |
58 | 60 | using v8::JustVoid; |
59 | 61 | using v8::Local; |
| 62 | +using v8::LocalVector; |
60 | 63 | using v8::Maybe; |
| 64 | +using v8::MaybeLocal; |
61 | 65 | using v8::Nothing; |
62 | 66 | using v8::Object; |
63 | 67 | using v8::PropertyAttribute; |
@@ -672,9 +676,6 @@ static void LoadCertsFromDir(std::vector<X509*>* certs, |
672 | 676 | return; |
673 | 677 | } |
674 | 678 |
|
675 | | - uv_fs_t stats_req; |
676 | | - auto cleanup_stats = |
677 | | - OnScopeLeave([&stats_req]() { uv_fs_req_cleanup(&stats_req); }); |
678 | 679 | for (;;) { |
679 | 680 | uv_dirent_t ent; |
680 | 681 |
|
@@ -691,12 +692,14 @@ static void LoadCertsFromDir(std::vector<X509*>* certs, |
691 | 692 | return; |
692 | 693 | } |
693 | 694 |
|
| 695 | + uv_fs_t stats_req; |
694 | 696 | std::string file_path = std::string(cert_dir) + "/" + ent.name; |
695 | 697 | int stats_r = uv_fs_stat(nullptr, &stats_req, file_path.c_str(), nullptr); |
696 | 698 | if (stats_r == 0 && |
697 | 699 | (static_cast<uv_stat_t*>(stats_req.ptr)->st_mode & S_IFREG)) { |
698 | 700 | LoadCertsFromFile(certs, file_path.c_str()); |
699 | 701 | } |
| 702 | + uv_fs_req_cleanup(&stats_req); |
700 | 703 | } |
701 | 704 | } |
702 | 705 |
|
@@ -775,7 +778,7 @@ static std::vector<X509*> InitializeSystemStoreCertificates() { |
775 | 778 | return system_store_certs; |
776 | 779 | } |
777 | 780 |
|
778 | | -static std::vector<X509*>& GetSystemStoreRootCertificates() { |
| 781 | +static std::vector<X509*>& GetSystemStoreCACertificates() { |
779 | 782 | // Use function-local static to guarantee thread safety. |
780 | 783 | static std::vector<X509*> system_store_certs = |
781 | 784 | InitializeSystemStoreCertificates(); |
@@ -847,7 +850,7 @@ X509_STORE* NewRootCertStore() { |
847 | 850 | CHECK_EQ(1, X509_STORE_add_cert(store, cert)); |
848 | 851 | } |
849 | 852 | if (per_process::cli_options->use_system_ca) { |
850 | | - for (X509* cert : GetSystemStoreRootCertificates()) { |
| 853 | + for (X509* cert : GetSystemStoreCACertificates()) { |
851 | 854 | CHECK_EQ(1, X509_STORE_add_cert(store, cert)); |
852 | 855 | } |
853 | 856 | } |
@@ -869,7 +872,7 @@ void CleanupCachedRootCertificates() { |
869 | 872 | } |
870 | 873 | } |
871 | 874 | if (has_cached_system_root_certs.load()) { |
872 | | - for (X509* cert : GetSystemStoreRootCertificates()) { |
| 875 | + for (X509* cert : GetSystemStoreCACertificates()) { |
873 | 876 | X509_free(cert); |
874 | 877 | } |
875 | 878 | } |
@@ -881,7 +884,7 @@ void CleanupCachedRootCertificates() { |
881 | 884 | } |
882 | 885 | } |
883 | 886 |
|
884 | | -void GetRootCertificates(const FunctionCallbackInfo<Value>& args) { |
| 887 | +void GetBundledRootCertificates(const FunctionCallbackInfo<Value>& args) { |
885 | 888 | Environment* env = Environment::GetCurrent(args); |
886 | 889 | Local<Value> result[arraysize(root_certs)]; |
887 | 890 |
|
@@ -898,6 +901,58 @@ void GetRootCertificates(const FunctionCallbackInfo<Value>& args) { |
898 | 901 | Array::New(env->isolate(), result, arraysize(root_certs))); |
899 | 902 | } |
900 | 903 |
|
| 904 | +MaybeLocal<Array> X509sToArrayOfStrings(Environment* env, |
| 905 | + const std::vector<X509*>& certs) { |
| 906 | + ClearErrorOnReturn clear_error_on_return; |
| 907 | + EscapableHandleScope scope(env->isolate()); |
| 908 | + |
| 909 | + LocalVector<Value> result(env->isolate(), certs.size()); |
| 910 | + for (size_t i = 0; i < certs.size(); ++i) { |
| 911 | + X509View view(certs[i]); |
| 912 | + auto pem_bio = view.toPEM(); |
| 913 | + if (!pem_bio) { |
| 914 | + ThrowCryptoError(env, ERR_get_error(), "X509 to PEM conversion"); |
| 915 | + return MaybeLocal<Array>(); |
| 916 | + } |
| 917 | + |
| 918 | + char* pem_data = nullptr; |
| 919 | + auto pem_size = BIO_get_mem_data(pem_bio.get(), &pem_data); |
| 920 | + if (pem_size <= 0 || !pem_data) { |
| 921 | + ThrowCryptoError(env, ERR_get_error(), "Reading PEM data"); |
| 922 | + return MaybeLocal<Array>(); |
| 923 | + } |
| 924 | + // PEM is base64-encoded, so it must be one-byte. |
| 925 | + if (!String::NewFromOneByte(env->isolate(), |
| 926 | + reinterpret_cast<uint8_t*>(pem_data), |
| 927 | + v8::NewStringType::kNormal, |
| 928 | + pem_size) |
| 929 | + .ToLocal(&result[i])) { |
| 930 | + return MaybeLocal<Array>(); |
| 931 | + } |
| 932 | + } |
| 933 | + return scope.Escape(Array::New(env->isolate(), result.data(), result.size())); |
| 934 | +} |
| 935 | + |
| 936 | +void GetSystemCACertificates(const FunctionCallbackInfo<Value>& args) { |
| 937 | + Environment* env = Environment::GetCurrent(args); |
| 938 | + Local<Array> results; |
| 939 | + if (X509sToArrayOfStrings(env, GetSystemStoreCACertificates()) |
| 940 | + .ToLocal(&results)) { |
| 941 | + args.GetReturnValue().Set(results); |
| 942 | + } |
| 943 | +} |
| 944 | + |
| 945 | +void GetExtraCACertificates(const FunctionCallbackInfo<Value>& args) { |
| 946 | + Environment* env = Environment::GetCurrent(args); |
| 947 | + if (extra_root_certs_file.empty()) { |
| 948 | + return args.GetReturnValue().Set(Array::New(env->isolate())); |
| 949 | + } |
| 950 | + Local<Array> results; |
| 951 | + if (X509sToArrayOfStrings(env, GetExtraCACertificates()).ToLocal(&results)) { |
| 952 | + args.GetReturnValue().Set(results); |
| 953 | + } |
| 954 | +} |
| 955 | + |
901 | 956 | bool SecureContext::HasInstance(Environment* env, const Local<Value>& value) { |
902 | 957 | return GetConstructorTemplate(env)->HasInstance(value); |
903 | 958 | } |
@@ -981,8 +1036,14 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) { |
981 | 1036 | GetConstructorTemplate(env), |
982 | 1037 | SetConstructorFunctionFlag::NONE); |
983 | 1038 |
|
| 1039 | + SetMethodNoSideEffect(context, |
| 1040 | + target, |
| 1041 | + "getBundledRootCertificates", |
| 1042 | + GetBundledRootCertificates); |
| 1043 | + SetMethodNoSideEffect( |
| 1044 | + context, target, "getSystemCACertificates", GetSystemCACertificates); |
984 | 1045 | SetMethodNoSideEffect( |
985 | | - context, target, "getRootCertificates", GetRootCertificates); |
| 1046 | + context, target, "getExtraCACertificates", GetExtraCACertificates); |
986 | 1047 | } |
987 | 1048 |
|
988 | 1049 | void SecureContext::RegisterExternalReferences( |
@@ -1022,7 +1083,9 @@ void SecureContext::RegisterExternalReferences( |
1022 | 1083 |
|
1023 | 1084 | registry->Register(CtxGetter); |
1024 | 1085 |
|
1025 | | - registry->Register(GetRootCertificates); |
| 1086 | + registry->Register(GetBundledRootCertificates); |
| 1087 | + registry->Register(GetSystemCACertificates); |
| 1088 | + registry->Register(GetExtraCACertificates); |
1026 | 1089 | } |
1027 | 1090 |
|
1028 | 1091 | SecureContext* SecureContext::Create(Environment* env) { |
|