tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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" {}