lib.rs (11492B)
1 /* -*- Mode: rust; rust-indent-offset: 4 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #![allow(non_snake_case)] 7 8 #[cfg(any(target_os = "macos", target_os = "ios"))] 9 #[macro_use] 10 extern crate core_foundation; 11 #[macro_use] 12 extern crate cstr; 13 #[cfg(any(target_os = "macos", target_os = "ios"))] 14 #[macro_use] 15 extern crate lazy_static; 16 #[macro_use] 17 extern crate xpcom; 18 19 use log::{debug, error, trace, warn}; 20 use nserror::{nsresult, NS_OK}; 21 use pkcs11_bindings::*; 22 use rsclientcerts::manager::{IsSearchingForClientCerts, Manager}; 23 use rsclientcerts::{ 24 declare_pkcs11_find_functions, declare_pkcs11_informational_functions, 25 declare_pkcs11_session_functions, declare_pkcs11_sign_functions, 26 declare_unsupported_pkcs11_functions, log_with_thread_id, 27 }; 28 use std::convert::TryInto; 29 use std::os::raw::c_char; 30 use std::sync::Mutex; 31 use xpcom::interfaces::{nsIObserverService, nsISupports}; 32 33 #[cfg(target_os = "android")] 34 mod backend_android; 35 #[cfg(any(target_os = "macos", target_os = "ios"))] 36 mod backend_macos; 37 #[cfg(all(target_os = "windows", not(target_arch = "aarch64")))] 38 mod backend_windows; 39 40 #[cfg(target_os = "android")] 41 use crate::backend_android::Backend; 42 #[cfg(any(target_os = "macos", target_os = "ios"))] 43 use crate::backend_macos::Backend; 44 #[cfg(all(target_os = "windows", not(target_arch = "aarch64")))] 45 use crate::backend_windows::Backend; 46 47 /// The singleton `Manager` that handles state with respect to PKCS#11. Only one thread may use it 48 /// at a time, but there is no restriction on which threads may use it. Note that the underlying OS 49 /// APIs may not necessarily be thread safe. For platforms where this is the case, the `Backend` 50 /// will synchronously run the relevant code on a background thread. 51 static MANAGER: Mutex<Option<Manager<Backend, IsGeckoSearchingForClientCerts>>> = Mutex::new(None); 52 53 // Obtaining a handle on the manager proxy is a two-step process. First the mutex must be locked, 54 // which (if successful), results in a mutex guard object. We must then get a mutable refence to the 55 // underlying manager proxy (if set - otherwise we return an error). This can't happen all in one 56 // macro without dropping a reference that needs to live long enough for this to be safe. In 57 // practice, this looks like: 58 // let mut manager_guard = try_to_get_manager_guard!(); 59 // let manager = manager_guard_to_manager!(manager_guard); 60 macro_rules! try_to_get_manager_guard { 61 () => { 62 match MANAGER.lock() { 63 Ok(maybe_manager) => maybe_manager, 64 Err(poison_error) => { 65 log_with_thread_id!( 66 error, 67 "previous thread panicked acquiring manager lock: {}", 68 poison_error 69 ); 70 return CKR_DEVICE_ERROR; 71 } 72 } 73 }; 74 } 75 76 macro_rules! manager_guard_to_manager { 77 ($manager_guard:ident) => { 78 match $manager_guard.as_mut() { 79 Some(manager) => manager, 80 None => { 81 log_with_thread_id!(error, "module state expected to be set, but it is not"); 82 return CKR_DEVICE_ERROR; 83 } 84 } 85 }; 86 } 87 88 #[xpcom(implement(nsIObserver), nonatomic)] 89 struct ShutdownObserver {} 90 91 impl ShutdownObserver { 92 xpcom_method!(observe => Observe(_subject: *const nsISupports, topic: *const c_char, _data: *const u16)); 93 /// Ensure any OS-backed resources are released on the proper thread before all non-main 94 /// threads are shut down. Also remove this observer. 95 fn observe( 96 &self, 97 _subject: &nsISupports, 98 topic: *const c_char, 99 _data: *const u16, 100 ) -> Result<(), nsresult> { 101 // Ignore errors since we're shutting down and there's no sensible way to handle them. 102 let _ = C_Finalize(std::ptr::null_mut()); 103 if let Ok(service) = xpcom::components::Observer::service::<nsIObserverService>() { 104 let _ = unsafe { service.RemoveObserver(self.coerce(), topic) }; 105 } 106 Ok(()) 107 } 108 } 109 110 extern "C" { 111 fn IsGeckoSearchingForClientAuthCertificates() -> bool; 112 } 113 114 struct IsGeckoSearchingForClientCerts; 115 116 impl IsSearchingForClientCerts for IsGeckoSearchingForClientCerts { 117 fn is_searching_for_client_certs() -> bool { 118 unsafe { IsGeckoSearchingForClientAuthCertificates() } 119 } 120 } 121 122 /// This gets called to initialize the module. For this implementation, this consists of 123 /// instantiating the `Manager`. 124 extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV { 125 // This will fail if this has already been called, but this isn't a problem because either way, 126 // logging has been initialized. 127 let _ = env_logger::try_init(); 128 129 #[cfg(target_os = "android")] 130 { 131 android_logger::init_once( 132 android_logger::Config::default().with_max_level(log::LevelFilter::Trace), 133 ); 134 } 135 136 let backend = match Backend::new() { 137 Ok(backend) => backend, 138 Err(e) => { 139 log_with_thread_id!(error, "C_Initialize: Backend::new() failed: {}", e); 140 return CKR_DEVICE_ERROR; 141 } 142 }; 143 let mut manager_guard = try_to_get_manager_guard!(); 144 match manager_guard.replace(Manager::new(vec![backend])) { 145 Some(_unexpected_previous_manager) => { 146 log_with_thread_id!( 147 warn, 148 "C_Initialize: replacing previously set module state (this is expected on macOS but not on Windows)" 149 ); 150 } 151 None => {} 152 } 153 154 // Register an observer to release any OS-backed resources on the background thread at shutdown, 155 // before the background thread goes away. Ideally this will have already happened due to 156 // nsNSSComponent shutting down, but if there are any lingering network connections, this module 157 // may not have been unloaded yet. 158 if let Ok(main_thread) = moz_task::get_main_thread() { 159 moz_task::spawn_onto("register shutdown observer", main_thread.coerce(), async { 160 if let Ok(service) = xpcom::components::Observer::service::<nsIObserverService>() { 161 let observer = ShutdownObserver::allocate(InitShutdownObserver {}); 162 unsafe { 163 let _ = service.AddObserver( 164 observer.coerce(), 165 cstr!("xpcom-shutdown").as_ptr(), 166 false, 167 ); 168 }; 169 } 170 }) 171 .detach(); 172 } 173 174 log_with_thread_id!(debug, "C_Initialize: CKR_OK"); 175 CKR_OK 176 } 177 178 extern "C" fn C_Finalize(_pReserved: CK_VOID_PTR) -> CK_RV { 179 let mut manager_guard = try_to_get_manager_guard!(); 180 match manager_guard.take() { 181 Some(_) => { 182 log_with_thread_id!(debug, "C_Finalize: CKR_OK"); 183 CKR_OK 184 } 185 None => { 186 log_with_thread_id!(debug, "C_Finalize: CKR_CRYPTOKI_NOT_INITIALIZED"); 187 CKR_CRYPTOKI_NOT_INITIALIZED 188 } 189 } 190 } 191 192 // The specification mandates that these strings be padded with spaces to the appropriate length. 193 // Since the length of fixed-size arrays in rust is part of the type, the compiler enforces that 194 // these byte strings are of the correct length. 195 const MANUFACTURER_ID_BYTES: &[u8; 32] = b"Mozilla Corporation "; 196 const LIBRARY_DESCRIPTION_BYTES: &[u8; 32] = b"OS Client Cert Module "; 197 198 declare_pkcs11_informational_functions!(); 199 declare_pkcs11_session_functions!(); 200 declare_pkcs11_find_functions!(); 201 declare_pkcs11_sign_functions!(); 202 declare_unsupported_pkcs11_functions!(); 203 204 /// To be a valid PKCS #11 module, this list of functions must be supported. At least cryptoki 2.2 205 /// must be supported for this module to work in NSS. 206 static FUNCTION_LIST: CK_FUNCTION_LIST = CK_FUNCTION_LIST { 207 version: CK_VERSION { major: 2, minor: 2 }, 208 C_Initialize: Some(C_Initialize), 209 C_Finalize: Some(C_Finalize), 210 C_GetInfo: Some(C_GetInfo), 211 C_GetFunctionList: None, 212 C_GetSlotList: Some(C_GetSlotList), 213 C_GetSlotInfo: Some(C_GetSlotInfo), 214 C_GetTokenInfo: Some(C_GetTokenInfo), 215 C_GetMechanismList: Some(C_GetMechanismList), 216 C_GetMechanismInfo: Some(C_GetMechanismInfo), 217 C_InitToken: Some(C_InitToken), 218 C_InitPIN: Some(C_InitPIN), 219 C_SetPIN: Some(C_SetPIN), 220 C_OpenSession: Some(C_OpenSession), 221 C_CloseSession: Some(C_CloseSession), 222 C_CloseAllSessions: Some(C_CloseAllSessions), 223 C_GetSessionInfo: Some(C_GetSessionInfo), 224 C_GetOperationState: Some(C_GetOperationState), 225 C_SetOperationState: Some(C_SetOperationState), 226 C_Login: Some(C_Login), 227 C_Logout: Some(C_Logout), 228 C_CreateObject: Some(C_CreateObject), 229 C_CopyObject: Some(C_CopyObject), 230 C_DestroyObject: Some(C_DestroyObject), 231 C_GetObjectSize: Some(C_GetObjectSize), 232 C_GetAttributeValue: Some(C_GetAttributeValue), 233 C_SetAttributeValue: Some(C_SetAttributeValue), 234 C_FindObjectsInit: Some(C_FindObjectsInit), 235 C_FindObjects: Some(C_FindObjects), 236 C_FindObjectsFinal: Some(C_FindObjectsFinal), 237 C_EncryptInit: Some(C_EncryptInit), 238 C_Encrypt: Some(C_Encrypt), 239 C_EncryptUpdate: Some(C_EncryptUpdate), 240 C_EncryptFinal: Some(C_EncryptFinal), 241 C_DecryptInit: Some(C_DecryptInit), 242 C_Decrypt: Some(C_Decrypt), 243 C_DecryptUpdate: Some(C_DecryptUpdate), 244 C_DecryptFinal: Some(C_DecryptFinal), 245 C_DigestInit: Some(C_DigestInit), 246 C_Digest: Some(C_Digest), 247 C_DigestUpdate: Some(C_DigestUpdate), 248 C_DigestKey: Some(C_DigestKey), 249 C_DigestFinal: Some(C_DigestFinal), 250 C_SignInit: Some(C_SignInit), 251 C_Sign: Some(C_Sign), 252 C_SignUpdate: Some(C_SignUpdate), 253 C_SignFinal: Some(C_SignFinal), 254 C_SignRecoverInit: Some(C_SignRecoverInit), 255 C_SignRecover: Some(C_SignRecover), 256 C_VerifyInit: Some(C_VerifyInit), 257 C_Verify: Some(C_Verify), 258 C_VerifyUpdate: Some(C_VerifyUpdate), 259 C_VerifyFinal: Some(C_VerifyFinal), 260 C_VerifyRecoverInit: Some(C_VerifyRecoverInit), 261 C_VerifyRecover: Some(C_VerifyRecover), 262 C_DigestEncryptUpdate: Some(C_DigestEncryptUpdate), 263 C_DecryptDigestUpdate: Some(C_DecryptDigestUpdate), 264 C_SignEncryptUpdate: Some(C_SignEncryptUpdate), 265 C_DecryptVerifyUpdate: Some(C_DecryptVerifyUpdate), 266 C_GenerateKey: Some(C_GenerateKey), 267 C_GenerateKeyPair: Some(C_GenerateKeyPair), 268 C_WrapKey: Some(C_WrapKey), 269 C_UnwrapKey: Some(C_UnwrapKey), 270 C_DeriveKey: Some(C_DeriveKey), 271 C_SeedRandom: Some(C_SeedRandom), 272 C_GenerateRandom: Some(C_GenerateRandom), 273 C_GetFunctionStatus: Some(C_GetFunctionStatus), 274 C_CancelFunction: Some(C_CancelFunction), 275 C_WaitForSlotEvent: Some(C_WaitForSlotEvent), 276 }; 277 278 /// # Safety 279 /// 280 /// This is the only function this module exposes. NSS calls it to obtain the list of functions 281 /// comprising this module. 282 /// ppFunctionList must be a valid pointer. 283 #[no_mangle] 284 pub unsafe extern "C" fn OSClientCerts_C_GetFunctionList( 285 ppFunctionList: CK_FUNCTION_LIST_PTR_PTR, 286 ) -> CK_RV { 287 if ppFunctionList.is_null() { 288 return CKR_ARGUMENTS_BAD; 289 } 290 // CK_FUNCTION_LIST_PTR is a *mut CK_FUNCTION_LIST, but as per the 291 // specification, the caller must treat it as *const CK_FUNCTION_LIST. 292 *ppFunctionList = std::ptr::addr_of!(FUNCTION_LIST) as CK_FUNCTION_LIST_PTR; 293 CKR_OK 294 } 295 296 #[cfg_attr( 297 any(target_os = "macos", target_os = "ios"), 298 link(name = "Security", kind = "framework") 299 )] 300 extern "C" {}