/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use log::{debug, error, trace, warn}; use nserror::{nsresult, NS_OK}; use pkcs11_bindings::*; use rsclientcerts::manager::{IsSearchingForClientCerts, Manager}; use rsclientcerts::{
declare_pkcs11_find_functions, declare_pkcs11_informational_functions,
declare_pkcs11_session_functions, declare_pkcs11_sign_functions,
declare_unsupported_pkcs11_functions, log_with_thread_id,
}; use std::convert::TryInto; use std::os::raw::c_char; use std::sync::Mutex; use xpcom::interfaces::{nsIObserverService, nsISupports};
#[cfg(target_os = "android")] mod backend_android; #[cfg(any(target_os = "macos", target_os = "ios"))] mod backend_macos; #[cfg(all(target_os = "windows", not(target_arch = "aarch64")))] mod backend_windows;
/// The singleton `Manager` that handles state with respect to PKCS#11. Only one thread may use it /// at a time, but there is no restriction on which threads may use it. Note that the underlying OS /// APIs may not necessarily be thread safe. For platforms where this is the case, the `Backend` /// will synchronously run the relevant code on a background thread. static MANAGER: Mutex<Option<Manager<Backend, IsGeckoSearchingForClientCerts>>> = Mutex::new(None);
// Obtaining a handle on the manager proxy is a two-step process. First the mutex must be locked, // which (if successful), results in a mutex guard object. We must then get a mutable refence to the // underlying manager proxy (if set - otherwise we return an error). This can't happen all in one // macro without dropping a reference that needs to live long enough for this to be safe. In // practice, this looks like: // let mut manager_guard = try_to_get_manager_guard!(); // let manager = manager_guard_to_manager!(manager_guard);
macro_rules! try_to_get_manager_guard {
() => { match MANAGER.lock() {
Ok(maybe_manager) => maybe_manager,
Err(poison_error) => {
log_with_thread_id!(
error, "previous thread panicked acquiring manager lock: {}",
poison_error
); return CKR_DEVICE_ERROR;
}
}
};
}
macro_rules! manager_guard_to_manager {
($manager_guard:ident) => { match $manager_guard.as_mut() {
Some(manager) => manager,
None => {
log_with_thread_id!(error, "module state expected to be set, but it is not"); return CKR_DEVICE_ERROR;
}
}
};
}
impl ShutdownObserver {
xpcom_method!(observe => Observe(_subject: *const nsISupports, topic: *const c_char, _data: *const u16)); /// Ensure any OS-backed resources are released on the proper thread before all non-main /// threads are shut down. Also remove this observer. fn observe(
&self,
_subject: &nsISupports,
topic: *const c_char,
_data: *const u16,
) -> Result<(), nsresult> { // Ignore errors since we're shutting down and there's no sensible way to handle them. let _ = C_Finalize(std::ptr::null_mut()); iflet Ok(service) = xpcom::components::Observer::service::<nsIObserverService>() { let _ = unsafe { service.RemoveObserver(self.coerce(), topic) };
}
Ok(())
}
}
/// This gets called to initialize the module. For this implementation, this consists of /// instantiating the `Manager`. extern"C"fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV { // This will fail if this has already been called, but this isn't a problem because either way, // logging has been initialized. let _ = env_logger::try_init();
let backend = match Backend::new() {
Ok(backend) => backend,
Err(e) => {
log_with_thread_id!(error, "C_Initialize: Backend::new() failed: {}", e); return CKR_DEVICE_ERROR;
}
}; letmut manager_guard = try_to_get_manager_guard!(); match manager_guard.replace(Manager::new(vec![backend])) {
Some(_unexpected_previous_manager) => {
log_with_thread_id!(
warn, "C_Initialize: replacing previously set module state (this is expected on macOS but not on Windows)"
);
}
None => {}
}
// Register an observer to release any OS-backed resources on the background thread at shutdown, // before the background thread goes away. Ideally this will have already happened due to // nsNSSComponent shutting down, but if there are any lingering network connections, this module // may not have been unloaded yet. iflet Ok(main_thread) = moz_task::get_main_thread() {
moz_task::spawn_onto("register shutdown observer", main_thread.coerce(), async { iflet Ok(service) = xpcom::components::Observer::service::<nsIObserverService>() { let observer = ShutdownObserver::allocate(InitShutdownObserver {}); unsafe { let _ = service.AddObserver(
observer.coerce(),
cstr!("xpcom-shutdown").as_ptr(), false,
);
};
}
})
.detach();
}
// The specification mandates that these strings be padded with spaces to the appropriate length. // Since the length of fixed-size arrays in rust is part of the type, the compiler enforces that // these byte strings are of the correct length. const MANUFACTURER_ID_BYTES: &[u8; 32] = b"Mozilla Corporation "; const LIBRARY_DESCRIPTION_BYTES: &[u8; 32] = b"OS Client Cert Module ";
/// # Safety /// /// This is the only function this module exposes. NSS calls it to obtain the list of functions /// comprising this module. /// ppFunctionList must be a valid pointer. #[no_mangle] pubunsafeextern"C"fn OSClientCerts_C_GetFunctionList(
ppFunctionList: CK_FUNCTION_LIST_PTR_PTR,
) -> CK_RV { if ppFunctionList.is_null() { return CKR_ARGUMENTS_BAD;
} // CK_FUNCTION_LIST_PTR is a *mut CK_FUNCTION_LIST, but as per the // specification, the caller must treat it as *const CK_FUNCTION_LIST.
*ppFunctionList = std::ptr::addr_of!(FUNCTION_LIST) as CK_FUNCTION_LIST_PTR;
CKR_OK
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.