Merge pull request #76836 from Faless/tls/system_certs
[TLS] Add support for platform-specific CA bundles.
This commit is contained in:
commit
258fabdbb3
17 changed files with 180 additions and 13 deletions
|
@ -217,7 +217,11 @@ opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and be
|
|||
opts.Add("build_profile", "Path to a file containing a feature build profile", "")
|
||||
opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
|
||||
opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
|
||||
opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
|
||||
opts.Add(
|
||||
"system_certs_path",
|
||||
"Use this path as TLS certificates default for editor and Linux/BSD export templates (for package maintainers)",
|
||||
"",
|
||||
)
|
||||
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
|
||||
|
||||
# Thirdparty libraries
|
||||
|
|
|
@ -137,6 +137,7 @@ public:
|
|||
virtual String get_stdin_string() = 0;
|
||||
|
||||
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) = 0; // Should return cryptographically-safe random bytes.
|
||||
virtual String get_system_ca_certificates() { return ""; } // Concatenated certificates in PEM format.
|
||||
|
||||
virtual PackedStringArray get_connected_midi_inputs();
|
||||
virtual void open_midi_inputs();
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/io/certs_compressed.gen.h"
|
||||
#include "core/io/compression.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_settings.h"
|
||||
|
@ -337,20 +338,26 @@ void CryptoMbedTLS::load_default_certificates(String p_path) {
|
|||
if (!p_path.is_empty()) {
|
||||
// Use certs defined in project settings.
|
||||
default_certs->load(p_path);
|
||||
}
|
||||
} else {
|
||||
// Try to use system certs otherwise.
|
||||
String system_certs = OS::get_singleton()->get_system_ca_certificates();
|
||||
if (!system_certs.is_empty()) {
|
||||
CharString cs = system_certs.utf8();
|
||||
default_certs->load_from_memory((const uint8_t *)cs.get_data(), cs.size());
|
||||
print_verbose("Loaded system CA certificates");
|
||||
}
|
||||
#ifdef BUILTIN_CERTS_ENABLED
|
||||
else {
|
||||
// Use builtin certs only if user did not override it in project settings.
|
||||
PackedByteArray out;
|
||||
out.resize(_certs_uncompressed_size + 1);
|
||||
Compression::decompress(out.ptrw(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
|
||||
out.write[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
|
||||
#ifdef DEBUG_ENABLED
|
||||
print_verbose("Loaded builtin certs");
|
||||
else {
|
||||
// Use builtin certs if there are no system certs.
|
||||
PackedByteArray certs;
|
||||
certs.resize(_certs_uncompressed_size + 1);
|
||||
Compression::decompress(certs.ptrw(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
|
||||
certs.write[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
|
||||
default_certs->load_from_memory(certs.ptr(), certs.size());
|
||||
print_verbose("Loaded builtin CA certificates");
|
||||
}
|
||||
#endif
|
||||
default_certs->load_from_memory(out.ptr(), out.size());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) {
|
||||
|
|
|
@ -1044,6 +1044,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
|||
return PermissionsUtil.getGrantedPermissions(getActivity());
|
||||
}
|
||||
|
||||
@Keep
|
||||
private String getCACertificates() {
|
||||
return GodotNetUtils.getCACertificates();
|
||||
}
|
||||
|
||||
/**
|
||||
* The download state should trigger changes in the UI --- it may be useful
|
||||
* to show the state as being indeterminate at times. This sample can be
|
||||
|
|
|
@ -33,11 +33,17 @@ package org.godotengine.godot.utils;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* This class handles Android-specific networking functions.
|
||||
* For now, it only provides access to WifiManager.MulticastLock, which is needed on some devices
|
||||
* It provides access to the CA certificates KeyStore, and the WifiManager.MulticastLock, which is needed on some devices
|
||||
* to receive broadcast and multicast packets.
|
||||
*/
|
||||
public class GodotNetUtils {
|
||||
|
@ -79,4 +85,34 @@ public class GodotNetUtils {
|
|||
Log.e("Godot", "Exception during multicast lock release: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of trusted CA certificates from the "AndroidCAStore" and returns them in PRM format.
|
||||
* @see https://developer.android.com/reference/java/security/KeyStore .
|
||||
* @return A string of concatenated X509 certificates in PEM format.
|
||||
*/
|
||||
public static String getCACertificates() {
|
||||
try {
|
||||
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
|
||||
StringBuilder writer = new StringBuilder();
|
||||
|
||||
if (ks != null) {
|
||||
ks.load(null, null);
|
||||
Enumeration<String> aliases = ks.aliases();
|
||||
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String)aliases.nextElement();
|
||||
|
||||
X509Certificate cert = (X509Certificate)ks.getCertificate(alias);
|
||||
writer.append("-----BEGIN CERTIFICATE-----\n");
|
||||
writer.append(Base64.encodeToString(cert.getEncoded(), Base64.DEFAULT));
|
||||
writer.append("-----END CERTIFICATE-----\n");
|
||||
}
|
||||
}
|
||||
return writer.toString();
|
||||
} catch (Exception e) {
|
||||
Log.e("Godot", "Exception while reading CA certificates: " + e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
|
|||
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
|
||||
_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
|
||||
_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
|
||||
_get_ca_certificates = p_env->GetMethodID(godot_class, "getCACertificates", "()Ljava/lang/String;");
|
||||
_init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V");
|
||||
_get_surface = p_env->GetMethodID(godot_class, "getSurface", "()Landroid/view/Surface;");
|
||||
_is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z");
|
||||
|
@ -310,6 +311,17 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
|
|||
return permissions_list;
|
||||
}
|
||||
|
||||
String GodotJavaWrapper::get_ca_certificates() const {
|
||||
if (_get_ca_certificates) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, String());
|
||||
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates);
|
||||
return jstring_to_string(s, env);
|
||||
} else {
|
||||
return String();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotJavaWrapper::init_input_devices() {
|
||||
if (_init_input_devices) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
|
|
@ -60,6 +60,7 @@ private:
|
|||
jmethodID _request_permission = nullptr;
|
||||
jmethodID _request_permissions = nullptr;
|
||||
jmethodID _get_granted_permissions = nullptr;
|
||||
jmethodID _get_ca_certificates = nullptr;
|
||||
jmethodID _init_input_devices = nullptr;
|
||||
jmethodID _get_surface = nullptr;
|
||||
jmethodID _is_activity_resumed = nullptr;
|
||||
|
@ -98,6 +99,7 @@ public:
|
|||
bool request_permission(const String &p_name);
|
||||
bool request_permissions();
|
||||
Vector<String> get_granted_permissions() const;
|
||||
String get_ca_certificates() const;
|
||||
void init_input_devices();
|
||||
jobject get_surface();
|
||||
bool is_activity_resumed();
|
||||
|
|
|
@ -757,6 +757,10 @@ Error OS_Android::kill(const ProcessID &p_pid) {
|
|||
return OS_Unix::kill(p_pid);
|
||||
}
|
||||
|
||||
String OS_Android::get_system_ca_certificates() {
|
||||
return godot_java->get_ca_certificates();
|
||||
}
|
||||
|
||||
Error OS_Android::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
|
||||
r_project_path = get_user_data_dir();
|
||||
Error err = OS_Unix::setup_remote_filesystem(p_server_host, p_port, p_password, r_project_path);
|
||||
|
|
|
@ -160,6 +160,7 @@ public:
|
|||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
||||
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
|
||||
virtual Error kill(const ProcessID &p_pid) override;
|
||||
virtual String get_system_ca_certificates() override;
|
||||
|
||||
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "os_linuxbsd.h"
|
||||
|
||||
#include "core/io/certs_compressed.gen.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "main/main.h"
|
||||
#include "servers/display_server.h"
|
||||
|
@ -1085,6 +1086,40 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
String OS_LinuxBSD::get_system_ca_certificates() {
|
||||
String certfile;
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
// Compile time preferred certificates path.
|
||||
if (!String(_SYSTEM_CERTS_PATH).is_empty() && da->file_exists(_SYSTEM_CERTS_PATH)) {
|
||||
certfile = _SYSTEM_CERTS_PATH;
|
||||
} else if (da->file_exists("/etc/ssl/certs/ca-certificates.crt")) {
|
||||
// Debian/Ubuntu
|
||||
certfile = "/etc/ssl/certs/ca-certificates.crt";
|
||||
} else if (da->file_exists("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")) {
|
||||
// Fedora
|
||||
certfile = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem";
|
||||
} else if (da->file_exists("/etc/ca-certificates/extracted/tls-ca-bundle.pem")) {
|
||||
// Arch Linux
|
||||
certfile = "/etc/ca-certificates/extracted/tls-ca-bundle.pem";
|
||||
} else if (da->file_exists("/var/lib/ca-certificates/ca-bundle.pem")) {
|
||||
// openSUSE
|
||||
certfile = "/var/lib/ca-certificates/ca-bundle.pem";
|
||||
} else if (da->file_exists("/etc/ssl/cert.pem")) {
|
||||
// FreeBSD/OpenBSD
|
||||
certfile = "/etc/ssl/cert.pem";
|
||||
}
|
||||
|
||||
if (certfile.is_empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(certfile, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), "", vformat("Failed to open system CA certificates file: '%s'", certfile));
|
||||
|
||||
return f->get_as_text();
|
||||
}
|
||||
|
||||
OS_LinuxBSD::OS_LinuxBSD() {
|
||||
main_loop = nullptr;
|
||||
|
||||
|
|
|
@ -133,6 +133,8 @@ public:
|
|||
|
||||
virtual Error move_to_trash(const String &p_path) override;
|
||||
|
||||
virtual String get_system_ca_certificates() override;
|
||||
|
||||
OS_LinuxBSD();
|
||||
~OS_LinuxBSD();
|
||||
};
|
||||
|
|
|
@ -235,6 +235,8 @@ def configure(env: "Environment"):
|
|||
"CoreMedia",
|
||||
"-framework",
|
||||
"QuartzCore",
|
||||
"-framework",
|
||||
"Security",
|
||||
]
|
||||
)
|
||||
env.Append(LIBS=["pthread", "z"])
|
||||
|
|
|
@ -119,6 +119,8 @@ public:
|
|||
|
||||
virtual Error move_to_trash(const String &p_path) override;
|
||||
|
||||
virtual String get_system_ca_certificates() override;
|
||||
|
||||
void run();
|
||||
|
||||
OS_MacOS();
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "os_macos.h"
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/version_generated.gen.h"
|
||||
#include "main/main.h"
|
||||
|
||||
|
@ -671,6 +672,34 @@ Error OS_MacOS::move_to_trash(const String &p_path) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
String OS_MacOS::get_system_ca_certificates() {
|
||||
CFArrayRef result;
|
||||
SecCertificateRef item;
|
||||
CFDataRef der;
|
||||
|
||||
OSStatus ret = SecTrustCopyAnchorCertificates(&result);
|
||||
ERR_FAIL_COND_V(ret != noErr, "");
|
||||
|
||||
CFIndex l = CFArrayGetCount(result);
|
||||
String certs;
|
||||
PackedByteArray pba;
|
||||
for (CFIndex i = 0; i < l; i++) {
|
||||
item = (SecCertificateRef)CFArrayGetValueAtIndex(result, i);
|
||||
der = SecCertificateCopyData(item);
|
||||
int derlen = CFDataGetLength(der);
|
||||
if (pba.size() < derlen * 3) {
|
||||
pba.resize(derlen * 3);
|
||||
}
|
||||
size_t b64len = 0;
|
||||
Error err = CryptoCore::b64_encode(pba.ptrw(), pba.size(), &b64len, (unsigned char *)CFDataGetBytePtr(der), derlen);
|
||||
CFRelease(der);
|
||||
ERR_CONTINUE(err != OK);
|
||||
certs += "-----BEGIN CERTIFICATE-----\n" + String((char *)pba.ptr(), b64len) + "\n-----END CERTIFICATE-----\n";
|
||||
}
|
||||
CFRelease(result);
|
||||
return certs;
|
||||
}
|
||||
|
||||
void OS_MacOS::run() {
|
||||
if (!main_loop) {
|
||||
return;
|
||||
|
|
|
@ -413,6 +413,7 @@ def configure_msvc(env, vcvars_msvc_config):
|
|||
"dxguid",
|
||||
"imm32",
|
||||
"bcrypt",
|
||||
"Crypt32",
|
||||
"Avrt",
|
||||
"dwmapi",
|
||||
"dwrite",
|
||||
|
@ -592,6 +593,7 @@ def configure_mingw(env):
|
|||
"ksuser",
|
||||
"imm32",
|
||||
"bcrypt",
|
||||
"crypt32",
|
||||
"avrt",
|
||||
"uuid",
|
||||
"dwmapi",
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include <regstr.h>
|
||||
#include <shlobj.h>
|
||||
#include <wbemcli.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#pragma pack(push, before_imagehlp, 8)
|
||||
|
@ -1675,6 +1676,26 @@ Error OS_Windows::move_to_trash(const String &p_path) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
String OS_Windows::get_system_ca_certificates() {
|
||||
HCERTSTORE cert_store = CertOpenSystemStoreA(0, "ROOT");
|
||||
ERR_FAIL_COND_V_MSG(!cert_store, "", "Failed to read the root certificate store.");
|
||||
|
||||
String certs;
|
||||
PCCERT_CONTEXT curr = CertEnumCertificatesInStore(cert_store, nullptr);
|
||||
while (curr) {
|
||||
DWORD size = 0;
|
||||
bool success = CryptBinaryToStringA(curr->pbCertEncoded, curr->cbCertEncoded, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, nullptr, &size);
|
||||
ERR_CONTINUE(!success);
|
||||
PackedByteArray pba;
|
||||
pba.resize(size);
|
||||
CryptBinaryToStringA(curr->pbCertEncoded, curr->cbCertEncoded, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, (char *)pba.ptrw(), &size);
|
||||
certs += String((char *)pba.ptr(), size);
|
||||
curr = CertEnumCertificatesInStore(cert_store, curr);
|
||||
}
|
||||
CertCloseStore(cert_store, 0);
|
||||
return certs;
|
||||
}
|
||||
|
||||
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
|
||||
hInstance = _hInstance;
|
||||
|
||||
|
|
|
@ -226,6 +226,8 @@ public:
|
|||
|
||||
virtual Error move_to_trash(const String &p_path) override;
|
||||
|
||||
virtual String get_system_ca_certificates() override;
|
||||
|
||||
void set_main_window(HWND p_main_window) { main_window = p_main_window; }
|
||||
|
||||
HINSTANCE get_hinstance() { return hInstance; }
|
||||
|
|
Loading…
Reference in a new issue