fn load_inner()

in pkcs11/pkcs11/src/context.rs [65:259]


    fn load_inner(lib_path: &std::path::Path) -> Result<Self, LoadContextError> {
        unsafe {
            let mut library =
                crate::dl::Library::load(lib_path).map_err(LoadContextError::LoadLibrary)?;

            let C_GetFunctionList: pkcs11_sys::CK_C_GetFunctionList = *library
                .symbol(std::ffi::CStr::from_bytes_with_nul(b"C_GetFunctionList\0").unwrap())
                .map_err(LoadContextError::LoadGetFunctionListSymbol)?;

            let mut function_list = std::ptr::null();
            let result = C_GetFunctionList(&mut function_list);
            if result != pkcs11_sys::CKR_OK {
                return Err(LoadContextError::GetFunctionListFailed(
                    format!("C_GetFunctionList failed with {result}").into(),
                ));
            }
            if function_list.is_null() {
                return Err(LoadContextError::GetFunctionListFailed(
                    "C_GetFunctionList succeeded but function list is still NULL".into(),
                ));
            }

            let version = {
                // We don't know if `*function_list` is actually a valid `CK_FUNCTION_LIST` yet,
                // because we need to look at its `.version` first.
                //
                // Miri considers it UB to read the `.version` from a `CK_FUNCTION_LIST` pointer directly
                // in case the pointee is smaller than a `CK_FUNCTION_LIST`, even if the pointee still has
                // `version` as its first field. Miri considers it safe if we cast the pointer to
                // the `version` field's type first and dereference just that.
                //
                // Note that C requires there to be no padding before the first field of a struct,
                // so this cast is valid in that regard.

                let version = function_list.cast::<pkcs11_sys::CK_VERSION>();
                let version = *version;
                if version.major != 2 || version.minor < 1 {
                    // We require 2.20 or higher. However opensc-pkcs11spy self-reports as v2.11 in the initial `CK_FUNCTION_LIST` version,
                    // and at least one smartcard vendor's library self-reports as v2.01 in the initial `CK_FUNCTION_LIST` version.
                    // Both of these report the real version in the `C_GetInfo` call (in opensc-pkcs11spy's case, it forwards `C_GetInfo` to
                    // the underlying PKCS#11 library), so we check the result of that later.
                    //
                    // Here the check here is a more lax v2.01 check, just to be sure that the pointer we have
                    // is to a `CK_FUNCTION_LIST` with the same structure that we expect. All PKCS#11 2.x versions
                    // have the same `CK_FUNCTION_LIST` structure with the same fields, so even if the final version from `C_GetInfo`
                    // turns out to be lower than 2.20, the `function_list` pointer is valid.
                    return Err(LoadContextError::UnsupportedPkcs11Version {
                        expected: pkcs11_sys::CK_VERSION { major: 2, minor: 1 },
                        actual: version,
                    });
                }

                version
            };

            let C_CloseSession = (*function_list)
                .C_CloseSession
                .ok_or(LoadContextError::MissingFunction("C_CloseSession"))?;
            let C_CreateObject = (*function_list)
                .C_CreateObject
                .ok_or(LoadContextError::MissingFunction("C_CreateObject"))?;
            let C_Decrypt = (*function_list)
                .C_Decrypt
                .ok_or(LoadContextError::MissingFunction("C_Decrypt"))?;
            let C_DecryptInit = (*function_list)
                .C_DecryptInit
                .ok_or(LoadContextError::MissingFunction("C_DecryptInit"))?;
            let C_DestroyObject = (*function_list)
                .C_DestroyObject
                .ok_or(LoadContextError::MissingFunction("C_DestroyObject"))?;
            let C_Encrypt = (*function_list)
                .C_Encrypt
                .ok_or(LoadContextError::MissingFunction("C_Encrypt"))?;
            let C_EncryptInit = (*function_list)
                .C_EncryptInit
                .ok_or(LoadContextError::MissingFunction("C_EncryptInit"))?;
            let C_Finalize = (*function_list).C_Finalize;
            let C_FindObjects = (*function_list)
                .C_FindObjects
                .ok_or(LoadContextError::MissingFunction("C_FindObjects"))?;
            let C_FindObjectsFinal = (*function_list)
                .C_FindObjectsFinal
                .ok_or(LoadContextError::MissingFunction("C_FindObjectsFinal"))?;
            let C_FindObjectsInit = (*function_list)
                .C_FindObjectsInit
                .ok_or(LoadContextError::MissingFunction("C_FindObjectsInit"))?;
            let C_GenerateKey = (*function_list)
                .C_GenerateKey
                .ok_or(LoadContextError::MissingFunction("C_GenerateKey"))?;
            let C_GenerateKeyPair = (*function_list)
                .C_GenerateKeyPair
                .ok_or(LoadContextError::MissingFunction("C_GenerateKeyPair"))?;
            let C_GetAttributeValue = (*function_list)
                .C_GetAttributeValue
                .ok_or(LoadContextError::MissingFunction("C_GetAttributeValue"))?;
            let C_SetAttributeValue = (*function_list)
                .C_SetAttributeValue
                .ok_or(LoadContextError::MissingFunction("C_SetAttributeValue"))?;
            let C_GetInfo = (*function_list).C_GetInfo;
            let C_GetSessionInfo = (*function_list)
                .C_GetSessionInfo
                .ok_or(LoadContextError::MissingFunction("C_GetSessionInfo"))?;
            let C_GetSlotList = (*function_list)
                .C_GetSlotList
                .ok_or(LoadContextError::MissingFunction("C_GetSlotList"))?;
            let C_GetTokenInfo = (*function_list)
                .C_GetTokenInfo
                .ok_or(LoadContextError::MissingFunction("C_GetTokenInfo"))?;
            let C_Login = (*function_list)
                .C_Login
                .ok_or(LoadContextError::MissingFunction("C_Login"))?;
            let C_OpenSession = (*function_list)
                .C_OpenSession
                .ok_or(LoadContextError::MissingFunction("C_OpenSession"))?;
            let C_Sign = (*function_list)
                .C_Sign
                .ok_or(LoadContextError::MissingFunction("C_Sign"))?;
            let C_SignInit = (*function_list)
                .C_SignInit
                .ok_or(LoadContextError::MissingFunction("C_SignInit"))?;
            let C_Verify = (*function_list)
                .C_Verify
                .ok_or(LoadContextError::MissingFunction("C_Verify"))?;
            let C_VerifyInit = (*function_list)
                .C_VerifyInit
                .ok_or(LoadContextError::MissingFunction("C_VerifyInit"))?;

            let C_Initialize = (*function_list)
                .C_Initialize
                .ok_or(LoadContextError::MissingFunction("C_Initialize"))?;
            let initialize_args = pkcs11_sys::CK_C_INITIALIZE_ARGS {
                CreateMutex: create_mutex,
                DestroyMutex: destroy_mutex,
                LockMutex: lock_mutex,
                UnlockMutex: unlock_mutex,
                flags: pkcs11_sys::CKF_LIBRARY_CANT_CREATE_OS_THREADS
                    | pkcs11_sys::CKF_OS_LOCKING_OK,
                pReserved: std::ptr::null_mut(),
            };
            let result = C_Initialize(&initialize_args);
            if result != pkcs11_sys::CKR_OK {
                return Err(LoadContextError::InitializeFailed(result));
            }

            // Now that `C_Initialize` has succeeded, create the `Context` value before doing anything else,
            // so that its `Drop` will run `C_Finalize` in case anything else fails later.
            let context = Context {
                sessions: Default::default(),

                _library: library,

                C_CloseSession,
                C_CreateObject,
                C_Decrypt,
                C_DecryptInit,
                C_DestroyObject,
                C_Encrypt,
                C_EncryptInit,
                C_Finalize,
                C_FindObjects,
                C_FindObjectsFinal,
                C_FindObjectsInit,
                C_GenerateKey,
                C_GenerateKeyPair,
                C_GetAttributeValue,
                C_SetAttributeValue,
                C_GetInfo,
                C_GetSessionInfo,
                C_GetSlotList,
                C_GetTokenInfo,
                C_Login,
                C_OpenSession,
                C_Sign,
                C_SignInit,
                C_Verify,
                C_VerifyInit,
            };

            let version = context.info().map_or(
                version, // Doesn't support C_GetInfo, so the initial version in the CK_FUNCTION_LIST is all we have.
                |info| info.cryptokiVersion,
            );
            if version.major != 2 || version.minor < 20 {
                return Err(LoadContextError::UnsupportedPkcs11Version {
                    expected: pkcs11_sys::CK_VERSION {
                        major: 2,
                        minor: 20,
                    },
                    actual: version,
                });
            }

            Ok(context)
        }
    }