-
Notifications
You must be signed in to change notification settings - Fork 892
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #585 from inejge/cacert
Improve rustls CA certificate loading
- Loading branch information
Showing
10 changed files
with
395 additions
and
10 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "ca-loader" | ||
version = "0.1.0" | ||
authors = [ "Ivan Nejgebauer <inejge@gmail.com>" ] | ||
|
||
[dependencies] | ||
libc = "0.2" | ||
|
||
[target."cfg(windows)".dependencies] | ||
winapi = "0.2.8" | ||
crypt32-sys = "0.2" | ||
|
||
[target.'cfg(target_os = "macos")'.dependencies] | ||
security-framework = "0.1.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#[macro_use] | ||
mod macros; | ||
mod sys; | ||
|
||
pub use self::sys::CertBundle; | ||
|
||
pub enum CertItem { | ||
File(String), | ||
Blob(Vec<u8>) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Taken from the libc crate, see <https://github.com/rust-lang/libc> for | ||
// authorship and copyright information. | ||
|
||
// A macro for defining #[cfg] if-else statements. | ||
// | ||
// This is similar to the `if/elif` C preprocessor macro by allowing definition | ||
// of a cascade of `#[cfg]` cases, emitting the implementation which matches | ||
// first. | ||
// | ||
// This allows you to conveniently provide a long list #[cfg]'d blocks of code | ||
// without having to rewrite each clause multiple times. | ||
macro_rules! cfg_if { | ||
($( | ||
if #[cfg($($meta:meta),*)] { $($it:item)* } | ||
) else * else { | ||
$($it2:item)* | ||
}) => { | ||
__cfg_if_items! { | ||
() ; | ||
$( ( ($($meta),*) ($($it)*) ), )* | ||
( () ($($it2)*) ), | ||
} | ||
} | ||
} | ||
|
||
macro_rules! __cfg_if_items { | ||
(($($not:meta,)*) ; ) => {}; | ||
(($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { | ||
__cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* } | ||
__cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } | ||
} | ||
} | ||
|
||
macro_rules! __cfg_if_apply { | ||
($m:meta, $($it:item)*) => { | ||
$(#[$m] $it)* | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
extern crate security_framework as sf; | ||
|
||
use super::super::CertItem; | ||
use self::sf::item::{ItemClass, ItemSearchOptions, Reference, SearchResult}; | ||
use self::sf::keychain::SecKeychain; | ||
use self::sf::os::macos::keychain::SecKeychainExt; | ||
use std::i32; | ||
use std::result::Result; | ||
|
||
pub struct CertBundle { | ||
rv: Vec<SearchResult> | ||
} | ||
|
||
pub struct CertIter { | ||
it: Box<Iterator<Item=SearchResult>> | ||
} | ||
|
||
impl IntoIterator for CertBundle { | ||
type Item = CertItem; | ||
type IntoIter = CertIter; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
CertIter { it: Box::new(self.rv.into_iter()) } | ||
} | ||
} | ||
|
||
impl Iterator for CertIter { | ||
type Item = CertItem; | ||
|
||
fn next(&mut self) -> Option<CertItem> { | ||
if let Some(res) = self.it.next() { | ||
if let Some(ref rref) = res.reference { | ||
match rref { | ||
&Reference::Certificate(ref cert) => return Some(CertItem::Blob(cert.to_der())), | ||
_ => () | ||
} | ||
} | ||
return self.next(); | ||
} | ||
None | ||
} | ||
} | ||
|
||
impl CertBundle { | ||
pub fn new() -> Result<CertBundle, ()> { | ||
let root_kc = try!(SecKeychain::open("/System/Library/Keychains/SystemRootCertificates.keychain").map_err(|_| ())); | ||
let chains = [ root_kc ]; | ||
let mut opts = ItemSearchOptions::new(); | ||
let opts = opts.keychains(&chains).class(ItemClass::Certificate).load_refs(true).limit(i32::MAX as i64); | ||
let rv = try!(opts.search().map_err(|_| ())); | ||
Ok(CertBundle { rv: rv }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
cfg_if! { | ||
if #[cfg(windows)] { | ||
mod windows; | ||
pub use self::windows::CertBundle; | ||
} else if #[cfg(target_os = "macos")] { | ||
mod macos; | ||
pub use self::macos::CertBundle; | ||
} else if #[cfg(unix)] { | ||
mod unix; | ||
pub use self::unix::CertBundle; | ||
} else { | ||
// Unknown | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
extern crate libc; | ||
|
||
use std::ffi::CStr; | ||
use std::fs; | ||
use std::mem; | ||
use std::result::Result; | ||
use super::super::CertItem; | ||
|
||
cfg_if! { | ||
if #[cfg(any(target_os = "android", target_os = "solaris"))] { | ||
use std::fs::{read_dir, ReadDir}; | ||
|
||
pub struct CertBundle(&'static str); | ||
|
||
pub struct CertIter(&'static str, Option<ReadDir>); | ||
|
||
impl IntoIterator for CertBundle { | ||
type Item = CertItem; | ||
type IntoIter = CertIter; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
if let Ok(dir) = read_dir(self.0) { | ||
CertIter(self.0, Some(dir)) | ||
} else { | ||
CertIter(self.0, None) | ||
} | ||
} | ||
} | ||
|
||
impl Iterator for CertIter { | ||
type Item = CertItem; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
match self.1 { | ||
None => return None, | ||
Some(ref mut dir) => { | ||
match dir.next() { | ||
None => return None, | ||
Some(Err(_)) => return None, | ||
Some(Ok(ref de)) => { | ||
if let Ok(ftyp) = de.file_type() { | ||
if !ftyp.is_dir() { | ||
if let Some(s) = de.file_name().to_str() { | ||
let mut full_name = String::from(self.0); | ||
full_name.push('/'); | ||
full_name.push_str(s); | ||
return Some(CertItem::File(full_name)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
self.next() | ||
} | ||
} | ||
|
||
impl CertBundle { | ||
pub fn new() -> Result<CertBundle, ()> { | ||
Ok(CertBundle(try!(sys_path()))) | ||
} | ||
} | ||
} else { | ||
use std::option; | ||
|
||
pub struct CertBundle(Option<CertItem>); | ||
|
||
impl IntoIterator for CertBundle { | ||
type Item = CertItem; | ||
type IntoIter = option::IntoIter<CertItem>; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
self.0.into_iter() | ||
} | ||
} | ||
|
||
impl CertBundle { | ||
pub fn new() -> Result<CertBundle, ()> { | ||
Ok(CertBundle(Some(CertItem::File(try!(sys_path()).to_string())))) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn sys_path() -> Result<&'static str, ()> { | ||
// Why use mem::uninitialized()? If we didn't, we'd need a bunch of | ||
// #cfg's for OS variants, since the components of struct utsname are | ||
// fixed-size char arrays (so no generic initializers), and that size | ||
// is different across OSs. Furthermore, uname() doesn't care about | ||
// the contents of struct utsname on input, and will fill it with | ||
// properly NUL-terminated strings on successful return. | ||
unsafe { | ||
let mut uts = mem::uninitialized::<libc::utsname>(); | ||
|
||
if libc::uname(&mut uts) < 0 { | ||
return Err(()); | ||
} | ||
let sysname = try!(CStr::from_ptr(uts.sysname.as_ptr()).to_str().map_err(|_| ())); | ||
let release = try!(CStr::from_ptr(uts.release.as_ptr()).to_str().map_err(|_| ())); | ||
let path = match sysname { | ||
"FreeBSD" | "OpenBSD" => "/etc/ssl/cert.pem", | ||
"NetBSD" => "/etc/ssl/certs", | ||
"Linux" => linux_distro_guess_ca_path(), | ||
"SunOS" => { | ||
let major = release.split('.').take(1).collect::<String>(); | ||
let major = major.parse::<u32>().unwrap_or(5); | ||
let minor = release.split('.').skip(1).take(1).collect::<String>(); | ||
let minor = minor.parse::<u32>().unwrap_or(10); | ||
if major < 5 || (major == 5 && minor < 11) { | ||
"/opt/csw/share/cacertificates/mozilla" | ||
} else { | ||
"/etc/certs/CA" | ||
} | ||
} | ||
_ => unimplemented!() | ||
}; | ||
Ok(path) | ||
} | ||
} | ||
|
||
cfg_if! { | ||
if #[cfg(target_os = "android")] { | ||
fn linux_distro_guess_ca_path() -> &'static str { | ||
"/system/etc/security/cacerts" | ||
} | ||
} else { | ||
fn linux_distro_guess_ca_path() -> &'static str { | ||
if let Ok(_debian) = fs::metadata("/etc/debian_version") { | ||
"/etc/ssl/certs/ca-certificates.crt" | ||
} else if let Ok(_rh) = fs::metadata("/etc/redhat-release") { | ||
"/etc/pki/tls/certs/ca-bundle.crt" | ||
} else if let Ok(_suse) = fs::metadata("/etc/SuSE-release") { | ||
"/etc/ssl/ca-bundle.pem" | ||
} else { // fallback | ||
"/etc/pki/tls/cacert.pem" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
extern crate crypt32; | ||
extern crate winapi; | ||
|
||
use super::super::CertItem; | ||
use std::ffi::CString; | ||
use std::ptr; | ||
use std::result::Result; | ||
use std::slice::from_raw_parts; | ||
|
||
pub struct CertBundle { | ||
store: winapi::HCERTSTORE, | ||
ctx_p: winapi::PCCERT_CONTEXT | ||
} | ||
|
||
pub struct CertIter { | ||
bundle: CertBundle | ||
} | ||
|
||
impl IntoIterator for CertBundle { | ||
type Item = CertItem; | ||
type IntoIter = CertIter; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
CertIter { bundle: self } | ||
} | ||
} | ||
|
||
impl Iterator for CertIter { | ||
type Item = CertItem; | ||
|
||
fn next(&mut self) -> Option<CertItem> { | ||
if self.bundle.ctx_p.is_null() { | ||
return None; | ||
} | ||
unsafe { | ||
let ctx = *self.bundle.ctx_p; | ||
let enc_slice = from_raw_parts( | ||
ctx.pbCertEncoded as *const u8, | ||
ctx.cbCertEncoded as usize); | ||
let mut blob = Vec::with_capacity(ctx.cbCertEncoded as usize); | ||
blob.extend_from_slice(enc_slice); | ||
self.bundle.ctx_p = crypt32::CertEnumCertificatesInStore( | ||
self.bundle.store, | ||
self.bundle.ctx_p); | ||
Some(CertItem::Blob(blob)) | ||
} | ||
} | ||
} | ||
|
||
impl CertBundle { | ||
pub fn new() -> Result<CertBundle, ()> { | ||
unsafe { | ||
let store = crypt32::CertOpenSystemStoreA( | ||
0, | ||
CString::new("Root").unwrap().as_ptr() as winapi::LPCSTR); | ||
if store.is_null() { | ||
return Err(()); | ||
} | ||
let ctx_p = crypt32::CertEnumCertificatesInStore( | ||
store, | ||
ptr::null()); | ||
Ok(CertBundle { | ||
store: store, | ||
ctx_p: ctx_p | ||
}) | ||
} | ||
} | ||
} | ||
|
||
impl Drop for CertBundle { | ||
fn drop(&mut self) { | ||
unsafe { | ||
if !self.ctx_p.is_null() { | ||
crypt32::CertFreeCertificateContext(self.ctx_p); | ||
} | ||
crypt32::CertCloseStore(self.store, 0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.