mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
ssl: discover default certificate paths in host_env (#8649)
Keep host certificate path probing with the native certificate provider, select the first available OpenSSL-style file and directory, and leave environment overrides to ssl.py. This also gives Android string defaults instead of the previous None values. Co-authored-by: Ivan Mironov <mironov.ivan@gmail.com> Assisted-by: OpenAI Codex: GPT-5
This commit is contained in:
1 parent
d67526cb08
commit
4a267b2616
3 files changed
+57
-25
No files matched your search
Vendored
-1
@@ -762,7 +762,6 @@ class BasicSocketTests(unittest.TestCase):
|
||||
support.gc_collect()
|
||||
self.assertIn(r, str(cm.warning.args[0]))
|
||||
|
||||
@unittest.expectedFailureIf(sys.platform == "android", "TODO: RUSTPYTHON; TypeError: path should be string, bytes, os.PathLike or integer, not NoneType")
|
||||
def test_get_default_verify_paths(self):
|
||||
paths = ssl.get_default_verify_paths()
|
||||
self.assertEqual(len(paths), 6)
|
||||
|
||||
@@ -5,6 +5,43 @@ pub struct LoadResult {
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
const DEFAULT_CERT_FILES: &[&str] = &[
|
||||
"/etc/ssl/certs/ca-certificates.crt",
|
||||
"/etc/pki/tls/certs/ca-bundle.crt",
|
||||
"/etc/ssl/ca-bundle.pem",
|
||||
"/etc/pki/tls/cacert.pem",
|
||||
"/etc/ssl/cert.pem",
|
||||
"/usr/local/share/certs/ca-root-nss.crt",
|
||||
"/usr/local/etc/openssl/cert.pem",
|
||||
];
|
||||
|
||||
const DEFAULT_CERT_DIRS: &[&str] = &[
|
||||
"/etc/ssl/certs",
|
||||
"/etc/pki/tls/certs",
|
||||
"/system/etc/security/cacerts",
|
||||
"/usr/local/share/certs",
|
||||
];
|
||||
|
||||
fn first_existing<'a>(candidates: &'a [&'a str], exists: impl Fn(&str) -> bool) -> Option<&'a str> {
|
||||
candidates.iter().copied().find(|path| exists(path))
|
||||
}
|
||||
|
||||
/// Return the host's first available OpenSSL-style certificate file and directory.
|
||||
/// Environment overrides are intentionally left to Python's `ssl` module.
|
||||
pub fn default_verify_paths() -> (String, String) {
|
||||
if cfg!(windows) {
|
||||
return (String::new(), String::new());
|
||||
}
|
||||
|
||||
let cafile = first_existing(DEFAULT_CERT_FILES, |path| crate::fs::is_file(path))
|
||||
.unwrap_or("/etc/ssl/cert.pem")
|
||||
.to_owned();
|
||||
let capath = first_existing(DEFAULT_CERT_DIRS, |path| crate::fs::is_dir(path))
|
||||
.unwrap_or("/etc/ssl/certs")
|
||||
.to_owned();
|
||||
(cafile, capath)
|
||||
}
|
||||
|
||||
pub fn load() -> LoadResult {
|
||||
let result = rustls_native_certs::load_native_certs();
|
||||
LoadResult {
|
||||
@@ -20,3 +57,18 @@ pub fn load() -> LoadResult {
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::first_existing;
|
||||
|
||||
#[test]
|
||||
fn first_existing_path_preserves_candidate_priority() {
|
||||
let candidates = ["missing", "first", "second"];
|
||||
assert_eq!(
|
||||
first_existing(&candidates, |path| path == "first" || path == "second"),
|
||||
Some("first")
|
||||
);
|
||||
assert_eq!(first_existing(&candidates, |_| false), None);
|
||||
}
|
||||
}
|
||||
@@ -4914,34 +4914,15 @@ mod _ssl {
|
||||
|
||||
#[pyfunction]
|
||||
fn get_default_verify_paths(vm: &VirtualMachine) -> PyObjectRef {
|
||||
// Return default certificate paths as a tuple
|
||||
// Lib/ssl.py expects: (openssl_cafile_env, openssl_cafile, openssl_capath_env, openssl_capath)
|
||||
// parts[0] = environment variable name for cafile
|
||||
// parts[1] = default cafile path
|
||||
// parts[2] = environment variable name for capath
|
||||
// parts[3] = default capath path
|
||||
|
||||
// Common default paths for different platforms
|
||||
// These match the first candidates that rustls-native-certs/openssl-probe checks
|
||||
let (default_cafile, default_capath): (Option<&str>, Option<&str>) = cfg_select! {
|
||||
// macOS primarily uses Keychain API, but provides fallback paths
|
||||
// for compatibility and when Keychain access fails
|
||||
target_os = "macos" => (Some("/etc/ssl/cert.pem"), Some("/etc/ssl/certs")),
|
||||
// Linux: matches openssl-probe's first candidate (/etc/ssl/cert.pem)
|
||||
// openssl-probe checks multiple locations at runtime, but we return
|
||||
// OpenSSL's compile-time default
|
||||
target_os = "linux" => (Some("/etc/ssl/cert.pem"), Some("/etc/ssl/certs")),
|
||||
// Windows uses certificate store, not file paths
|
||||
// Return empty strings to avoid None being passed to os.path.isfile()
|
||||
windows => (Some(""), Some("")),
|
||||
_ => (None, None),
|
||||
};
|
||||
let (default_cafile, default_capath) =
|
||||
rustpython_host_env::native_certs::default_verify_paths();
|
||||
|
||||
let tuple = vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_str("SSL_CERT_FILE").into(), // openssl_cafile_env
|
||||
default_cafile.map_or_else(|| vm.ctx.none(), |s| vm.ctx.new_str(s).into()), // openssl_cafile
|
||||
vm.ctx.new_str("SSL_CERT_DIR").into(), // openssl_capath_env
|
||||
default_capath.map_or_else(|| vm.ctx.none(), |s| vm.ctx.new_str(s).into()), // openssl_capath
|
||||
vm.ctx.new_str(default_cafile).into(), // openssl_cafile
|
||||
vm.ctx.new_str("SSL_CERT_DIR").into(), // openssl_capath_env
|
||||
vm.ctx.new_str(default_capath).into(), // openssl_capath
|
||||
]);
|
||||
|
||||
tuple.into()
|
||||
|
||||
Reference in new issue
Block a user