diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-15 16:37:08 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-17 16:30:22 -0600 |
| commit | 45df4d0d9b577fecee798d672695fe24ff57fb1b (patch) | |
| tree | 1b99bf645035b58e0d6db08c7a83521f41f7a75b /vendor/security-framework/src | |
| parent | f94f79608393d4ab127db63cc41668445ef6b243 (diff) | |
feat: migrate from Cedar to SpiceDB authorization system
This is a major architectural change that replaces the Cedar policy-based
authorization system with SpiceDB's relation-based authorization.
Key changes:
- Migrate from Rust to Go implementation
- Replace Cedar policies with SpiceDB schema and relationships
- Switch from envoy `ext_authz` with Cedar to SpiceDB permission checks
- Update build system and dependencies for Go ecosystem
- Maintain Envoy integration for external authorization
This change enables more flexible permission modeling through SpiceDB's
Google Zanzibar inspired relation-based system, supporting complex
hierarchical permissions that were difficult to express in Cedar.
Breaking change: Existing Cedar policies and Rust-based configuration
will no longer work and need to be migrated to SpiceDB schema.
Diffstat (limited to 'vendor/security-framework/src')
35 files changed, 0 insertions, 9638 deletions
diff --git a/vendor/security-framework/src/access_control.rs b/vendor/security-framework/src/access_control.rs deleted file mode 100644 index dfaa4541..00000000 --- a/vendor/security-framework/src/access_control.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! Access Control support. - -use std::ptr::{self, null}; - -use core_foundation::string::CFString; -use core_foundation::base::{TCFType, CFOptionFlags, kCFAllocatorDefault}; -use security_framework_sys::access_control::{ - SecAccessControlGetTypeID, SecAccessControlCreateWithFlags, - kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, - kSecAttrAccessibleWhenUnlockedThisDeviceOnly, - kSecAttrAccessibleWhenUnlocked, - kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, - kSecAttrAccessibleAfterFirstUnlock -}; -use security_framework_sys::base::{SecAccessControlRef, errSecParam}; -use crate::base::{Error, Result}; - -declare_TCFType! { - /// A type representing sec access control settings. - SecAccessControl, SecAccessControlRef -} -impl_TCFType!( - SecAccessControl, - SecAccessControlRef, - SecAccessControlGetTypeID -); - -unsafe impl Sync for SecAccessControl {} -unsafe impl Send for SecAccessControl {} - -/// Specify when an item is available. -pub enum ProtectionMode { - /// The data in the keychain can only be accessed when the device is - /// unlocked. Only available if a passcode is set on the device. - AccessibleWhenPasscodeSetThisDeviceOnly, - ///The data in the keychain item can be accessed only while the device is - /// unlocked by the user. - AccessibleWhenUnlockedThisDeviceOnly, - /// The data in the keychain item can be accessed only while the device is - /// unlocked by the user. - AccessibleWhenUnlocked, - /// The data in the keychain item cannot be accessed after a restart until - /// the device has been unlocked once by the user. - AccessibleAfterFirstUnlockThisDeviceOnly, - /// The data in the keychain item cannot be accessed after a restart until - /// the device has been unlocked once by the user. - AccessibleAfterFirstUnlock, -} - -impl SecAccessControl { - /// Create `AccessControl` object from flags - pub fn create_with_flags(flags: CFOptionFlags) -> Result<Self> { - Self::create_with_protection(None, flags) - } - - /// Create `AccessControl` object from a protection value and flags. - pub fn create_with_protection(protection: Option<ProtectionMode>, flags: CFOptionFlags) -> Result<Self> { - let protection_val = protection.map(|v| { - match v { - ProtectionMode::AccessibleWhenPasscodeSetThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) }, - ProtectionMode::AccessibleWhenUnlockedThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) }, - ProtectionMode::AccessibleWhenUnlocked => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenUnlocked) }, - ProtectionMode::AccessibleAfterFirstUnlockThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) }, - ProtectionMode::AccessibleAfterFirstUnlock => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleAfterFirstUnlock) }, - } - }); - unsafe { - let access_control = SecAccessControlCreateWithFlags( - kCFAllocatorDefault, - protection_val.map(|v| v.as_CFTypeRef()).unwrap_or(null()), - flags, - ptr::null_mut(), - ); - if access_control.is_null() { - Err(Error::from_code(errSecParam)) - } else { - Ok(Self::wrap_under_create_rule(access_control)) - } - } - } -} diff --git a/vendor/security-framework/src/authorization.rs b/vendor/security-framework/src/authorization.rs deleted file mode 100644 index 762addb6..00000000 --- a/vendor/security-framework/src/authorization.rs +++ /dev/null @@ -1,815 +0,0 @@ -//! Authorization Services support. - -/// # Potential improvements -/// -/// * When generic specialization stabilizes prevent copying from `CString` -/// arguments. -/// * `AuthorizationCopyRightsAsync` -/// * Provide constants for well known item names - -use crate::base::{Error, Result}; -#[cfg(all(target_os = "macos", feature = "job-bless"))] -use core_foundation::base::Boolean; -use core_foundation::base::{CFTypeRef, TCFType}; -use core_foundation::bundle::CFBundleRef; -use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; -#[cfg(all(target_os = "macos", feature = "job-bless"))] -use core_foundation::error::CFError; -#[cfg(all(target_os = "macos", feature = "job-bless"))] -use core_foundation::error::CFErrorRef; -use core_foundation::string::{CFString, CFStringRef}; -use security_framework_sys::authorization as sys; -use security_framework_sys::base::errSecConversionError; -use std::ffi::{CStr, CString}; -use std::fs::File; -use std::mem::MaybeUninit; -use std::os::raw::c_void; -use std::ptr::addr_of; -use std::marker::PhantomData; -use sys::AuthorizationExternalForm; - -macro_rules! optional_str_to_cfref { - ($string:ident) => {{ - $string - .map(CFString::new) - .map_or(std::ptr::null(), |cfs| cfs.as_concrete_TypeRef()) - }}; -} - -macro_rules! cstring_or_err { - ($x:expr) => {{ - CString::new($x).map_err(|_| Error::from_code(errSecConversionError)) - }}; -} - -bitflags::bitflags! { - /// The flags used to specify authorization options. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct Flags: sys::AuthorizationFlags { - /// An empty flag set that you use as a placeholder when you don't want - /// any of the other flags. - const DEFAULTS = sys::kAuthorizationFlagDefaults; - - /// A flag that permits user interaction as needed. - const INTERACTION_ALLOWED = sys::kAuthorizationFlagInteractionAllowed; - - /// A flag that permits the Security Server to attempt to grant the - /// rights requested. - const EXTEND_RIGHTS = sys::kAuthorizationFlagExtendRights; - - /// A flag that permits the Security Server to grant rights on an - /// individual basis. - const PARTIAL_RIGHTS = sys::kAuthorizationFlagPartialRights; - - /// A flag that instructs the Security Server to revoke authorization. - const DESTROY_RIGHTS = sys::kAuthorizationFlagDestroyRights; - - /// A flag that instructs the Security Server to preauthorize the rights - /// requested. - const PREAUTHORIZE = sys::kAuthorizationFlagPreAuthorize; - } -} - -impl Default for Flags { - #[inline(always)] - fn default() -> Flags { - Flags::DEFAULTS - } -} - -/// Information about an authorization right or the environment. -#[repr(C)] -pub struct AuthorizationItem(sys::AuthorizationItem); - -impl AuthorizationItem { - /// The required name of the authorization right or environment data. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - #[must_use] pub fn name(&self) -> &str { - unsafe { - CStr::from_ptr(self.0.name) - .to_str() - .expect("AuthorizationItem::name failed to convert &str to CStr") - } - } - - /// The information pertaining to the name field. Do not rely on NULL - /// termination of string data. - #[inline] - #[must_use] pub fn value(&self) -> Option<&[u8]> { - if self.0.value.is_null() { - return None; - } - - let value = - unsafe { std::slice::from_raw_parts(self.0.value as *const u8, self.0.valueLength) }; - - Some(value) - } -} - -/// A set of authorization items returned and owned by the Security Server. -#[derive(Debug)] -#[repr(C)] -pub struct AuthorizationItemSet<'a> { - inner: *const sys::AuthorizationItemSet, - phantom: PhantomData<&'a sys::AuthorizationItemSet>, -} - -impl<'a> Drop for AuthorizationItemSet<'a> { - #[inline] - fn drop(&mut self) { - unsafe { - sys::AuthorizationFreeItemSet(self.inner as *mut sys::AuthorizationItemSet); - } - } -} - -/// Used by `AuthorizationItemSetBuilder` to store data pointed to by -/// `sys::AuthorizationItemSet`. -#[derive(Debug)] -pub struct AuthorizationItemSetStorage { - /// The layout of this is a little awkward because of the requirements of - /// Apple's APIs. `items` contains pointers to data owned by `names` and - /// `values`, so we must not modify them once `items` has been set up. - names: Vec<CString>, - values: Vec<Option<Vec<u8>>>, - items: Vec<sys::AuthorizationItem>, - - /// Must not be given to APIs which would attempt to modify it. - /// - /// See `AuthorizationItemSet` for sets owned by the Security Server which - /// are writable. - pub set: sys::AuthorizationItemSet, -} - -impl Default for AuthorizationItemSetStorage { - #[inline] - fn default() -> Self { - AuthorizationItemSetStorage { - names: Vec::new(), - values: Vec::new(), - items: Vec::new(), - set: sys::AuthorizationItemSet { - count: 0, - items: std::ptr::null_mut(), - }, - } - } -} - -/// A convenience `AuthorizationItemSetBuilder` builder which enabled you to use -/// rust types. All names and values passed in will be copied. -#[derive(Debug, Default)] -pub struct AuthorizationItemSetBuilder { - storage: AuthorizationItemSetStorage, -} - -// Stores AuthorizationItems contiguously, and their items separately -impl AuthorizationItemSetBuilder { - /// Creates a new `AuthorizationItemSetStore`, which simplifies creating - /// owned vectors of `AuthorizationItem`s. - #[inline(always)] - #[must_use] - pub fn new() -> AuthorizationItemSetBuilder { - Default::default() - } - - /// Adds an `AuthorizationItem` with the name set to a right and an empty - /// value. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn add_right<N: Into<Vec<u8>>>(mut self, name: N) -> Result<Self> { - self.storage.names.push(cstring_or_err!(name)?); - self.storage.values.push(None); - Ok(self) - } - - /// Adds an `AuthorizationItem` with arbitrary data. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn add_data<N, V>(mut self, name: N, value: V) -> Result<Self> - where - N: Into<Vec<u8>>, - V: Into<Vec<u8>>, - { - self.storage.names.push(cstring_or_err!(name)?); - self.storage.values.push(Some(value.into())); - Ok(self) - } - - /// Adds an `AuthorizationItem` with NULL terminated string data. - /// - /// If `name` or `value` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn add_string<N, V>(mut self, name: N, value: V) -> Result<Self> - where - N: Into<Vec<u8>>, - V: Into<Vec<u8>>, - { - self.storage.names.push(cstring_or_err!(name)?); - self.storage - .values - .push(Some(cstring_or_err!(value)?.to_bytes().to_vec())); - Ok(self) - } - - /// Creates the `sys::AuthorizationItemSet`, and gives you ownership of the - /// data it points to. - #[must_use] - pub fn build(mut self) -> AuthorizationItemSetStorage { - self.storage.items = self - .storage - .names - .iter() - .zip(self.storage.values.iter()) - .map(|(n, v)| sys::AuthorizationItem { - name: n.as_ptr(), - value: v - .as_ref() - .map_or(std::ptr::null_mut(), |v| v.as_ptr() as *mut c_void), - valueLength: v.as_ref().map_or(0, |v| v.len()), - flags: 0, - }) - .collect(); - - self.storage.set = sys::AuthorizationItemSet { - count: self.storage.items.len() as u32, - items: self.storage.items.as_ptr() as *mut sys::AuthorizationItem, - }; - - self.storage - } -} - -/// Used by `Authorization::set_item` to define the rules of he right. -#[derive(Copy, Clone)] -pub enum RightDefinition<'a> { - /// The dictionary will contain the keys and values that define the rules. - FromDictionary(&'a CFDictionary<CFStringRef, CFTypeRef>), - - /// The specified right's rules will be duplicated. - FromExistingRight(&'a str), -} - -/// A wrapper around `AuthorizationCreate` and functions which operate on an -/// `AuthorizationRef`. -#[derive(Debug)] -pub struct Authorization { - handle: sys::AuthorizationRef, - free_flags: Flags, -} - -impl TryFrom<AuthorizationExternalForm> for Authorization { - type Error = Error; - - /// Internalizes the external representation of an authorization reference. - #[cold] - fn try_from(external_form: AuthorizationExternalForm) -> Result<Self> { - let mut handle = MaybeUninit::<sys::AuthorizationRef>::uninit(); - - let status = unsafe { - sys::AuthorizationCreateFromExternalForm(&external_form, handle.as_mut_ptr()) - }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from_code(status)); - } - - let auth = Authorization { - handle: unsafe { handle.assume_init() }, - free_flags: Flags::default(), - }; - - Ok(auth) - } -} - -impl Authorization { - /// Creates an authorization object which has no environment or associated - /// rights. - #[inline] - #[allow(clippy::should_implement_trait)] - pub fn default() -> Result<Self> { - Self::new(None, None, Default::default()) - } - - /// Creates an authorization reference and provides an option to authorize - /// or preauthorize rights. - /// - /// `rights` should be the names of the rights you want to create. - /// - /// `environment` is used when authorizing or preauthorizing rights. Not - /// used in OS X v10.2 and earlier. In macOS 10.3 and later, you can pass - /// icon or prompt data to be used in the authentication dialog box. In - /// macOS 10.4 and later, you can also pass a user name and password in - /// order to authorize a user without user interaction. - #[allow(clippy::unnecessary_cast)] - pub fn new( - // FIXME: this should have been by reference - rights: Option<AuthorizationItemSetStorage>, - environment: Option<AuthorizationItemSetStorage>, - flags: Flags, - ) -> Result<Self> { - let rights_ptr = rights.as_ref().map_or(std::ptr::null(), |r| { - addr_of!(r.set) as *const sys::AuthorizationItemSet - }); - - let env_ptr = environment.as_ref().map_or(std::ptr::null(), |e| { - addr_of!(e.set) as *const sys::AuthorizationItemSet - }); - - let mut handle = MaybeUninit::<sys::AuthorizationRef>::uninit(); - - let status = unsafe { - sys::AuthorizationCreate(rights_ptr, env_ptr, flags.bits(), handle.as_mut_ptr()) - }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from_code(status)); - } - - Ok(Authorization { - handle: unsafe { handle.assume_init() }, - free_flags: Default::default(), - }) - } - - /// Internalizes the external representation of an authorization reference. - #[deprecated(since = "2.0.1", note = "Please use the TryFrom trait instead")] - pub fn from_external_form(external_form: sys::AuthorizationExternalForm) -> Result<Self> { - external_form.try_into() - } - - /// By default the rights acquired will be retained by the Security Server. - /// Use this to ensure they are destroyed and to prevent shared rights' - /// continued used by other processes. - #[inline(always)] - pub fn destroy_rights(mut self) { - self.free_flags = Flags::DESTROY_RIGHTS; - } - - /// Retrieve's the right's definition as a dictionary. Use `right_exists` - /// if you want to avoid retrieving the dictionary. - /// - /// `name` can be a wildcard right name. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn get_right<T: Into<Vec<u8>>>(name: T) -> Result<CFDictionary<CFString, CFTypeRef>> { - let name = cstring_or_err!(name)?; - let mut dict = MaybeUninit::<CFDictionaryRef>::uninit(); - - let status = unsafe { sys::AuthorizationRightGet(name.as_ptr(), dict.as_mut_ptr()) }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from_code(status)); - } - - let dict = unsafe { CFDictionary::wrap_under_create_rule(dict.assume_init()) }; - - Ok(dict) - } - - /// Checks if a right exists within the policy database. This is the same as - /// `get_right`, but avoids a dictionary allocation. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn right_exists<T: Into<Vec<u8>>>(name: T) -> Result<bool> { - let name = cstring_or_err!(name)?; - - let status = unsafe { sys::AuthorizationRightGet(name.as_ptr(), std::ptr::null_mut()) }; - - Ok(status == sys::errAuthorizationSuccess) - } - - /// Removes a right from the policy database. - /// - /// `name` cannot be a wildcard right name. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn remove_right<T: Into<Vec<u8>>>(&self, name: T) -> Result<()> { - let name = cstring_or_err!(name)?; - - let status = unsafe { sys::AuthorizationRightRemove(self.handle, name.as_ptr()) }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from_code(status)); - } - - Ok(()) - } - - /// Creates or updates a right entry in the policy database. Your process - /// must have a code signature in order to be able to add rights to the - /// authorization database. - /// - /// `name` cannot be a wildcard right. - /// - /// `definition` can be either a `CFDictionaryRef` containing keys defining - /// the rules or a `CFStringRef` representing the name of another right - /// whose rules you wish to duplicaate. - /// - /// `description` is a key which can be used to look up localized - /// descriptions. - /// - /// `bundle` will be used to get localizations from if not the main bundle. - /// - /// `localeTableName` will be used to get localizations if provided. - /// - /// If `name` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn set_right<T: Into<Vec<u8>>>( - &self, - name: T, - definition: RightDefinition<'_>, - description: Option<&str>, - bundle: Option<CFBundleRef>, - locale: Option<&str>, - ) -> Result<()> { - let name = cstring_or_err!(name)?; - - let definition_cfstring: CFString; - let definition_ref = match definition { - RightDefinition::FromDictionary(def) => def.as_CFTypeRef(), - RightDefinition::FromExistingRight(def) => { - definition_cfstring = CFString::new(def); - definition_cfstring.as_CFTypeRef() - } - }; - - let status = unsafe { - sys::AuthorizationRightSet( - self.handle, - name.as_ptr(), - definition_ref, - optional_str_to_cfref!(description), - bundle.unwrap_or(std::ptr::null_mut()), - optional_str_to_cfref!(locale), - ) - }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from_code(status)); - } - - Ok(()) - } - - /// An authorization plugin can store the results of an authentication - /// operation by calling the `SetContextValue` function. You can then - /// retrieve this supporting data, such as the user name. - /// - /// `tag` should specify the type of data the Security Server should return. - /// If `None`, all available information is retreieved. - /// - /// If `tag` isn't convertable to a `CString` it will return - /// Err(errSecConversionError). - pub fn copy_info<T: Into<Vec<u8>>>(&self, tag: Option<T>) -> Result<AuthorizationItemSet<'_>> { - let tag_with_nul: CString; - - let tag_ptr = match tag { - Some(tag) => { - tag_with_nul = cstring_or_err!(tag)?; - tag_with_nul.as_ptr() - } - None => std::ptr::null(), - }; - - let mut inner = MaybeUninit::<*mut sys::AuthorizationItemSet>::uninit(); - - let status = - unsafe { sys::AuthorizationCopyInfo(self.handle, tag_ptr, inner.as_mut_ptr()) }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from(status)); - } - - let set = AuthorizationItemSet { - inner: unsafe { inner.assume_init() }, - phantom: PhantomData, - }; - - Ok(set) - } - - /// Creates an external representation of an authorization reference so that - /// you can transmit it between processes. - pub fn make_external_form(&self) -> Result<sys::AuthorizationExternalForm> { - let mut external_form = MaybeUninit::<sys::AuthorizationExternalForm>::uninit(); - - let status = - unsafe { sys::AuthorizationMakeExternalForm(self.handle, external_form.as_mut_ptr()) }; - - if status != sys::errAuthorizationSuccess { - return Err(Error::from(status)); - } - - Ok(unsafe { external_form.assume_init() }) - } - - /// Runs an executable tool with root privileges. - /// Discards executable's output - #[cfg(target_os = "macos")] - #[inline(always)] - pub fn execute_with_privileges<P, S, I>( - &self, - command: P, - arguments: I, - flags: Flags, - ) -> Result<()> - where - P: AsRef<std::path::Path>, - I: IntoIterator<Item = S>, - S: AsRef<std::ffi::OsStr>, - { - use std::os::unix::ffi::OsStrExt; - - let arguments = arguments - .into_iter().flat_map(|a| CString::new(a.as_ref().as_bytes())) - .collect::<Vec<_>>(); - self.execute_with_privileges_internal(command.as_ref().as_os_str().as_bytes(), &arguments, flags, false)?; - Ok(()) - } - - /// Runs an executable tool with root privileges, - /// and returns a `File` handle to its communication pipe - #[cfg(target_os = "macos")] - #[inline(always)] - pub fn execute_with_privileges_piped<P, S, I>( - &self, - command: P, - arguments: I, - flags: Flags, - ) -> Result<File> - where - P: AsRef<std::path::Path>, - I: IntoIterator<Item = S>, - S: AsRef<std::ffi::OsStr>, - { - use std::os::unix::ffi::OsStrExt; - - let arguments = arguments - .into_iter().flat_map(|a| CString::new(a.as_ref().as_bytes())) - .collect::<Vec<_>>(); - Ok(self.execute_with_privileges_internal(command.as_ref().as_os_str().as_bytes(), &arguments, flags, true)?.unwrap()) - } - - /// Submits the executable for the given label as a `launchd` job. - #[cfg(all(target_os = "macos", feature = "job-bless"))] - pub fn job_bless(&self, label: &str) -> Result<(), CFError> { - #[link(name = "ServiceManagement", kind = "framework")] - extern "C" { - static kSMDomainSystemLaunchd: CFStringRef; - - fn SMJobBless( - domain: CFStringRef, - executableLabel: CFStringRef, - auth: sys::AuthorizationRef, - error: *mut CFErrorRef, - ) -> Boolean; - } - - unsafe { - let mut error = std::ptr::null_mut(); - SMJobBless( - kSMDomainSystemLaunchd, - CFString::new(label).as_concrete_TypeRef(), - self.handle, - &mut error, - ); - if !error.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - - Ok(()) - } - } - - // Runs an executable tool with root privileges. - #[cfg(target_os = "macos")] - fn execute_with_privileges_internal( - &self, - command: &[u8], - arguments: &[CString], - flags: Flags, - make_pipe: bool, - ) -> Result<Option<File>> { - use std::os::unix::io::{FromRawFd, RawFd}; - - let c_cmd = cstring_or_err!(command)?; - - let mut c_args = arguments.iter().map(|a| a.as_ptr() as _).collect::<Vec<_>>(); - c_args.push(std::ptr::null_mut()); - - let mut pipe: *mut libc::FILE = std::ptr::null_mut(); - - let status = unsafe { - sys::AuthorizationExecuteWithPrivileges( - self.handle, - c_cmd.as_ptr(), - flags.bits(), - c_args.as_ptr(), - if make_pipe { &mut pipe } else { std::ptr::null_mut() }, - ) - }; - - crate::cvt(status)?; - Ok(if make_pipe { - if pipe.is_null() { - return Err(Error::from_code(32)); // EPIPE? - } - Some(unsafe { File::from_raw_fd(libc::fileno(pipe) as RawFd) }) - } else { - None - }) - } -} - -impl Drop for Authorization { - #[inline] - fn drop(&mut self) { - unsafe { - sys::AuthorizationFree(self.handle, self.free_flags.bits()); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_create_default_authorization() { - Authorization::default().unwrap(); - } - - #[test] - fn test_create_allowed_authorization() -> Result<()> { - let rights = AuthorizationItemSetBuilder::new() - .add_right("system.hdd.smart")? - .add_right("system.login.done")? - .build(); - - Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap(); - - Ok(()) - } - - #[test] - fn test_create_then_destroy_allowed_authorization() -> Result<()> { - let rights = AuthorizationItemSetBuilder::new() - .add_right("system.hdd.smart")? - .add_right("system.login.done")? - .build(); - - let auth = Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap(); - auth.destroy_rights(); - - Ok(()) - } - - #[test] - fn test_create_authorization_requiring_interaction() -> Result<()> { - let rights = AuthorizationItemSetBuilder::new() - .add_right("system.privilege.admin")? - .build(); - - let error = Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap_err(); - - assert_eq!(error.code(), sys::errAuthorizationInteractionNotAllowed); - - Ok(()) - } - - fn create_credentials_env() -> Result<AuthorizationItemSetStorage> { - let set = AuthorizationItemSetBuilder::new() - .add_string("username", std::env::var("USER").expect("You must set the USER environment variable"))? - .add_string("password", std::env::var("PASSWORD").expect("You must set the PASSWORD environment varible"))? - .build(); - - Ok(set) - } - - #[test] - fn test_create_authorization_with_bad_credentials() -> Result<()> { - let rights = AuthorizationItemSetBuilder::new() - .add_right("system.privilege.admin")? - .build(); - - let env = AuthorizationItemSetBuilder::new() - .add_string("username", "Tim Apple")? - .add_string("password", "butterfly")? - .build(); - - let error = - Authorization::new(Some(rights), Some(env), Flags::INTERACTION_ALLOWED).unwrap_err(); - - assert_eq!(error.code(), sys::errAuthorizationDenied); - - Ok(()) - } - - #[test] - fn test_create_authorization_with_credentials() -> Result<()> { - if std::env::var_os("PASSWORD").is_none() { - return Ok(()); - } - - let rights = AuthorizationItemSetBuilder::new() - .add_right("system.privilege.admin")? - .build(); - - let env = create_credentials_env()?; - - Authorization::new(Some(rights), Some(env), Flags::EXTEND_RIGHTS).unwrap(); - - Ok(()) - } - - #[test] - fn test_query_authorization_database() -> Result<()> { - assert!(Authorization::right_exists("system.hdd.smart")?); - assert!(!Authorization::right_exists("EMPTY")?); - - let dict = Authorization::get_right("system.hdd.smart").unwrap(); - - let key = CFString::from_static_string("class"); - assert!(dict.contains_key(&key)); - - let invalid_key = CFString::from_static_string("EMPTY"); - assert!(!dict.contains_key(&invalid_key)); - - Ok(()) - } - - /// This test will only pass if its process has a valid code signature. - #[test] - fn test_modify_authorization_database() -> Result<()> { - if std::env::var_os("PASSWORD").is_none() { - return Ok(()); - } - - let rights = AuthorizationItemSetBuilder::new() - .add_right("config.modify.")? - .build(); - - let env = create_credentials_env()?; - - let auth = Authorization::new(Some(rights), Some(env), Flags::EXTEND_RIGHTS).unwrap(); - - assert!(!Authorization::right_exists("TEST_RIGHT")?); - - auth.set_right( - "TEST_RIGHT", - RightDefinition::FromExistingRight("system.hdd.smart"), - None, - None, - None, - ) - .unwrap(); - - assert!(Authorization::right_exists("TEST_RIGHT")?); - - auth.remove_right("TEST_RIGHT").unwrap(); - - assert!(!Authorization::right_exists("TEST_RIGHT")?); - - Ok(()) - } - - /// This test will succeed if authorization popup is approved. - #[test] - fn test_execute_with_privileges() -> Result<()> { - if std::env::var_os("PASSWORD").is_none() { - return Ok(()); - } - - let rights = AuthorizationItemSetBuilder::new() - .add_right("system.privilege.admin")? - .build(); - - let auth = Authorization::new( - Some(rights), - None, - Flags::DEFAULTS - | Flags::INTERACTION_ALLOWED - | Flags::PREAUTHORIZE - | Flags::EXTEND_RIGHTS, - )?; - - let file = auth.execute_with_privileges_piped("/bin/ls", ["/"], Flags::DEFAULTS)?; - - use std::io::{self, BufRead}; - for line in io::BufReader::new(file).lines() { - let _ = line.unwrap(); - } - - Ok(()) - } -} diff --git a/vendor/security-framework/src/base.rs b/vendor/security-framework/src/base.rs deleted file mode 100644 index e790d95c..00000000 --- a/vendor/security-framework/src/base.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Support types for other modules. - -use core_foundation::string::CFString; -use core_foundation_sys::base::OSStatus; -use std::error; -use std::fmt; -use std::num::NonZeroI32; -use std::result; - -/// A `Result` type commonly returned by functions. -pub type Result<T, E = Error> = result::Result<T, E>; - -/// A Security Framework error. -#[derive(Copy, Clone)] -pub struct Error(NonZeroI32); - -impl fmt::Debug for Error { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = fmt.debug_struct("Error"); - builder.field("code", &self.0); - if let Some(message) = self.message() { - builder.field("message", &message); - } - builder.finish() - } -} - -impl Error { - /// Creates a new `Error` from a status code. - /// The code must not be zero - #[inline] - #[must_use] - pub fn from_code(code: OSStatus) -> Self { - Self(NonZeroI32::new(code).unwrap_or_else(|| NonZeroI32::new(1).unwrap())) - } - - /// Returns a string describing the current error, if available. - #[inline(always)] - #[must_use] - pub fn message(self) -> Option<String> { - self.inner_message() - } - - #[cold] - fn inner_message(self) -> Option<String> { - use core_foundation::base::TCFType; - use security_framework_sys::base::SecCopyErrorMessageString; - use std::ptr; - - unsafe { - let s = SecCopyErrorMessageString(self.code(), ptr::null_mut()); - if s.is_null() { - None - } else { - Some(CFString::wrap_under_create_rule(s).to_string()) - } - } - } - - /// Returns the code of the current error. - #[inline(always)] - #[must_use] - pub fn code(self) -> OSStatus { - self.0.get() as _ - } -} - -impl From<OSStatus> for Error { - #[inline(always)] - #[must_use] - fn from(code: OSStatus) -> Self { - Self::from_code(code) - } -} - -impl fmt::Display for Error { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(message) = self.message() { - write!(fmt, "{message}") - } else { - write!(fmt, "error code {}", self.code()) - } - } -} - -impl error::Error for Error {} diff --git a/vendor/security-framework/src/certificate.rs b/vendor/security-framework/src/certificate.rs deleted file mode 100644 index 15fa297f..00000000 --- a/vendor/security-framework/src/certificate.rs +++ /dev/null @@ -1,320 +0,0 @@ -//! Certificate support. - -use core_foundation::array::{CFArray, CFArrayRef}; -use core_foundation::base::{TCFType, ToVoid}; -use core_foundation::data::CFData; -use core_foundation::dictionary::CFMutableDictionary; -use core_foundation::string::CFString; -use core_foundation_sys::base::kCFAllocatorDefault; -#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use security_framework_sys::base::{errSecNotTrusted, errSecSuccess}; -use security_framework_sys::base::{errSecParam, SecCertificateRef}; -use security_framework_sys::certificate::*; -use security_framework_sys::keychain_item::SecItemDelete; -use std::fmt; -use std::ptr; - -use crate::base::{Error, Result}; -use crate::cvt; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use crate::key; -#[cfg(target_os = "macos")] -use crate::os::macos::keychain::SecKeychain; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::base::FromVoid; -#[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::error::{CFError, CFErrorRef}; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::number::CFNumber; -#[cfg(feature = "serial-number-bigint")] -use num_bigint::BigUint; -use security_framework_sys::item::kSecValueRef; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use std::ops::Deref; - -declare_TCFType! { - /// A type representing a certificate. - SecCertificate, SecCertificateRef -} -impl_TCFType!(SecCertificate, SecCertificateRef, SecCertificateGetTypeID); - -unsafe impl Sync for SecCertificate {} -unsafe impl Send for SecCertificate {} - -impl fmt::Debug for SecCertificate { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("SecCertificate") - .field("subject", &self.subject_summary()) - .finish() - } -} - -impl SecCertificate { - /// Creates a `SecCertificate` from DER encoded certificate data. - pub fn from_der(der_data: &[u8]) -> Result<Self> { - let der_data = CFData::from_buffer(der_data); - unsafe { - let certificate = - SecCertificateCreateWithData(kCFAllocatorDefault, der_data.as_concrete_TypeRef()); - if certificate.is_null() { - Err(Error::from_code(errSecParam)) - } else { - Ok(Self::wrap_under_create_rule(certificate)) - } - } - } - - /// Returns DER encoded data describing this certificate. - #[must_use] - pub fn to_der(&self) -> Vec<u8> { - unsafe { - let der_data = SecCertificateCopyData(self.0); - CFData::wrap_under_create_rule(der_data).to_vec() - } - } - - /// Adds a certificate to a keychain. - #[cfg(target_os="macos")] - pub fn add_to_keychain(&self, keychain: Option<SecKeychain>) -> Result<()> { - let kch = match keychain { - Some(kch) => kch, - _ => SecKeychain::default()?, - }; - cvt(unsafe { - SecCertificateAddToKeychain(self.as_CFTypeRef() as *mut _, kch.as_CFTypeRef() as *mut _) - }) - } - - /// Returns a human readable summary of this certificate. - #[must_use] - pub fn subject_summary(&self) -> String { - unsafe { - let summary = SecCertificateCopySubjectSummary(self.0); - CFString::wrap_under_create_rule(summary).to_string() - } - } - - /// Returns a vector of email addresses for the subject of the certificate. - pub fn email_addresses(&self) -> Result<Vec<String>, Error> { - let mut array: CFArrayRef = ptr::null(); - unsafe { - cvt(SecCertificateCopyEmailAddresses( - self.as_concrete_TypeRef(), - &mut array, - ))?; - - let array = CFArray::<CFString>::wrap_under_create_rule(array); - Ok(array.into_iter().map(|p| p.to_string()).collect()) - } - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Returns DER encoded X.509 distinguished name of the certificate issuer. - #[must_use] - pub fn issuer(&self) -> Vec<u8> { - unsafe { - let issuer = SecCertificateCopyNormalizedIssuerSequence(self.0); - CFData::wrap_under_create_rule(issuer).to_vec() - } - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Returns DER encoded X.509 distinguished name of the certificate subject. - #[must_use] - pub fn subject(&self) -> Vec<u8> { - unsafe { - let subject = SecCertificateCopyNormalizedSubjectSequence(self.0); - CFData::wrap_under_create_rule(subject).to_vec() - } - } - - #[cfg(any(feature = "OSX_10_13", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Returns DER encoded serial number of the certificate. - pub fn serial_number_bytes(&self) -> Result<Vec<u8>, CFError> { - unsafe { - let mut error: CFErrorRef = ptr::null_mut(); - let serial_number = SecCertificateCopySerialNumberData(self.0, &mut error); - if error.is_null() { - Ok(CFData::wrap_under_create_rule(serial_number).to_vec()) - } else { - Err(CFError::wrap_under_create_rule(error)) - } - } - } - - /// Use `BigUint::from_bytes_be(serial_number_bytes())` instead - #[deprecated(note = "use serial_number_bytes()")] - #[cfg(feature = "serial-number-bigint")] - pub fn serial_number(&self) -> Result<BigUint, CFError> { - Ok(BigUint::from_bytes_be(&self.serial_number_bytes()?)) - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Returns DER encoded subjectPublicKeyInfo of certificate if available. This can be used - /// for certificate pinning. - pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> { - // Imported from TrustKit - // https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/TSKSPKIHashCache.m - let public_key = self.public_key()?; - Ok(self.pk_to_der(public_key)) - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - #[must_use] - fn pk_to_der(&self, public_key: key::SecKey) -> Option<Vec<u8>> { - use security_framework_sys::item::kSecAttrKeyType; - use security_framework_sys::item::kSecAttrKeySizeInBits; - - let public_key_attributes = public_key.attributes(); - let public_key_type = public_key_attributes - .find(unsafe { kSecAttrKeyType }.cast::<std::os::raw::c_void>())?; - let public_keysize = public_key_attributes - .find(unsafe { kSecAttrKeySizeInBits }.cast::<std::os::raw::c_void>())?; - let public_keysize = unsafe { CFNumber::from_void(*public_keysize.deref()) }; - let public_keysize_val = public_keysize.to_i64()? as u32; - let hdr_bytes = get_asn1_header_bytes( - unsafe { CFString::wrap_under_get_rule(*public_key_type.deref() as _) }, - public_keysize_val, - )?; - let public_key_data = public_key.external_representation()?; - let mut out = Vec::with_capacity(hdr_bytes.len() + public_key_data.len() as usize); - out.extend_from_slice(hdr_bytes); - out.extend_from_slice(public_key_data.bytes()); - Some(out) - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Get public key from certificate - pub fn public_key(&self) -> Result<key::SecKey> { - use crate::policy::SecPolicy; - use crate::trust::SecTrust; - use std::slice::from_ref; - - let policy = SecPolicy::create_x509(); - let mut trust = SecTrust::create_with_certificates(from_ref(self), from_ref(&policy))?; - #[allow(deprecated)] - #[cfg(not(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))] - trust.evaluate()?; - #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - cvt(match trust.evaluate_with_error() { - Ok(_) => errSecSuccess, - Err(_) => errSecNotTrusted, - })?; - trust.copy_public_key() - } - - /// Translates to `SecItemDelete`, passing in the `SecCertificateRef` - pub fn delete(&self) -> Result<(), Error> { - let query = CFMutableDictionary::from_CFType_pairs(&[( - unsafe { kSecValueRef }.to_void(), - self.to_void(), - )]); - - cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) }) - } -} - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -fn get_asn1_header_bytes(pkt: CFString, ksz: u32) -> Option<&'static [u8]> { - use security_framework_sys::item::kSecAttrKeyTypeRSA; - use security_framework_sys::item::kSecAttrKeyTypeECSECPrimeRandom; - - if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 2048 { - return Some(&RSA_2048_ASN1_HEADER); - } - if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 4096 { - return Some(&RSA_4096_ASN1_HEADER); - } - if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) } - && ksz == 256 - { - return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER); - } - if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) } - && ksz == 384 - { - return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER); - } - None -} - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -const RSA_2048_ASN1_HEADER: [u8; 24] = [ - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, -]; - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -const RSA_4096_ASN1_HEADER: [u8; 24] = [ - 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, -]; - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -const EC_DSA_SECP_256_R1_ASN1_HEADER: [u8; 26] = [ - 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, - 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, -]; - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -const EC_DSA_SECP_384_R1_ASN1_HEADER: [u8; 23] = [ - 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, - 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, -]; - -#[cfg(test)] -mod test { - use crate::test::certificate; - #[cfg(feature = "serial-number-bigint")] - use num_bigint::BigUint; - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - use x509_parser::prelude::*; - - #[test] - fn subject_summary() { - let cert = certificate(); - assert_eq!("foobar.com", cert.subject_summary()); - } - - #[test] - fn email_addresses() { - let cert = certificate(); - assert_eq!(Vec::<String>::new(), cert.email_addresses().unwrap()); - } - - #[test] - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - fn issuer() { - let cert = certificate(); - let issuer = cert.issuer(); - let (_, name) = X509Name::from_der(&issuer).unwrap(); - let name_str = name.to_string_with_registry(oid_registry()).unwrap(); - assert_eq!( - "C=US, ST=CALIFORNIA, L=PALO ALTO, O=FOOBAR LLC, OU=DEV LAND, CN=FOOBAR.COM", - name_str - ); - } - - #[test] - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - fn subject() { - let cert = certificate(); - let subject = cert.subject(); - let (_, name) = X509Name::from_der(&subject).unwrap(); - let name_str = name.to_string_with_registry(oid_registry()).unwrap(); - assert_eq!( - "C=US, ST=CALIFORNIA, L=PALO ALTO, O=FOOBAR LLC, OU=DEV LAND, CN=FOOBAR.COM", - name_str - ); - } - - #[test] - #[cfg(feature = "serial-number-bigint")] - #[allow(deprecated)] - fn serial_number() { - let cert = certificate(); - let serial_number = cert.serial_number().unwrap(); - assert_eq!(BigUint::from(16452297291294946383_u128), serial_number); - } -} diff --git a/vendor/security-framework/src/cipher_suite.rs b/vendor/security-framework/src/cipher_suite.rs deleted file mode 100644 index 4462b5e5..00000000 --- a/vendor/security-framework/src/cipher_suite.rs +++ /dev/null @@ -1,246 +0,0 @@ -//! Cipher Suites supported by Secure Transport - -use security_framework_sys::cipher_suite::*; - -macro_rules! make_suites { - ($($suite:ident),+) => { - /// TLS cipher suites. - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] - pub struct CipherSuite(SSLCipherSuite); - - #[allow(missing_docs)] - impl CipherSuite { - $( - pub const $suite: Self = Self($suite); - )+ - - #[inline(always)] - #[must_use] - pub fn from_raw(raw: SSLCipherSuite) -> Self { - Self(raw) - } - - #[inline(always)] - #[must_use] - pub fn to_raw(&self) -> SSLCipherSuite { - self.0 - } - } - } -} - -make_suites! { - // The commented out ones up here are aliases of the matching TLS suites - SSL_NULL_WITH_NULL_NULL, - SSL_RSA_WITH_NULL_MD5, - SSL_RSA_WITH_NULL_SHA, - SSL_RSA_EXPORT_WITH_RC4_40_MD5, - SSL_RSA_WITH_RC4_128_MD5, - SSL_RSA_WITH_RC4_128_SHA, - SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, - SSL_RSA_WITH_IDEA_CBC_SHA, - SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, - SSL_RSA_WITH_DES_CBC_SHA, - //SSL_RSA_WITH_3DES_EDE_CBC_SHA, - SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, - SSL_DH_DSS_WITH_DES_CBC_SHA, - //SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA, - SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, - SSL_DH_RSA_WITH_DES_CBC_SHA, - //SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA, - SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, - SSL_DHE_DSS_WITH_DES_CBC_SHA, - //SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, - SSL_DHE_RSA_WITH_DES_CBC_SHA, - //SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, - //SSL_DH_anon_WITH_RC4_128_MD5, - SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, - SSL_DH_anon_WITH_DES_CBC_SHA, - //SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, - SSL_FORTEZZA_DMS_WITH_NULL_SHA, - SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, - - /* TLS addenda using AES, per RFC 3268 */ - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DH_DSS_WITH_AES_128_CBC_SHA, - TLS_DH_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_DH_anon_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DH_DSS_WITH_AES_256_CBC_SHA, - TLS_DH_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_DH_anon_WITH_AES_256_CBC_SHA, - - /* ECDSA addenda, RFC 4492 */ - TLS_ECDH_ECDSA_WITH_NULL_SHA, - TLS_ECDH_ECDSA_WITH_RC4_128_SHA, - TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_NULL_SHA, - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDH_RSA_WITH_NULL_SHA, - TLS_ECDH_RSA_WITH_RC4_128_SHA, - TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_NULL_SHA, - TLS_ECDHE_RSA_WITH_RC4_128_SHA, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDH_anon_WITH_NULL_SHA, - TLS_ECDH_anon_WITH_RC4_128_SHA, - TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, - TLS_ECDH_anon_WITH_AES_128_CBC_SHA, - TLS_ECDH_anon_WITH_AES_256_CBC_SHA, - - /* TLS 1.2 addenda, RFC 5246 */ - - /* Initial state. */ - TLS_NULL_WITH_NULL_NULL, - - /* Server provided RSA certificate for key exchange. */ - TLS_RSA_WITH_NULL_MD5, - TLS_RSA_WITH_NULL_SHA, - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - //TLS_RSA_WITH_AES_128_CBC_SHA, - //TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_NULL_SHA256, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - - /* Server-authenticated (and optionally client-authenticated) Diffie-Hellman. */ - TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, - TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - //TLS_DH_DSS_WITH_AES_128_CBC_SHA, - //TLS_DH_RSA_WITH_AES_128_CBC_SHA, - //TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - //TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - //TLS_DH_DSS_WITH_AES_256_CBC_SHA, - //TLS_DH_RSA_WITH_AES_256_CBC_SHA, - //TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - //TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_DH_DSS_WITH_AES_128_CBC_SHA256, - TLS_DH_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DH_DSS_WITH_AES_256_CBC_SHA256, - TLS_DH_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - - /* Completely anonymous Diffie-Hellman */ - TLS_DH_anon_WITH_RC4_128_MD5, - TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, - //TLS_DH_anon_WITH_AES_128_CBC_SHA, - //TLS_DH_anon_WITH_AES_256_CBC_SHA, - TLS_DH_anon_WITH_AES_128_CBC_SHA256, - TLS_DH_anon_WITH_AES_256_CBC_SHA256, - - /* Addendum from RFC 4279, TLS PSK */ - - TLS_PSK_WITH_RC4_128_SHA, - TLS_PSK_WITH_3DES_EDE_CBC_SHA, - TLS_PSK_WITH_AES_128_CBC_SHA, - TLS_PSK_WITH_AES_256_CBC_SHA, - TLS_DHE_PSK_WITH_RC4_128_SHA, - TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA, - TLS_RSA_PSK_WITH_RC4_128_SHA, - TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_PSK_WITH_AES_128_CBC_SHA, - TLS_RSA_PSK_WITH_AES_256_CBC_SHA, - - /* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */ - - TLS_PSK_WITH_NULL_SHA, - TLS_DHE_PSK_WITH_NULL_SHA, - TLS_RSA_PSK_WITH_NULL_SHA, - - /* Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites - for TLS. */ - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_DH_RSA_WITH_AES_128_GCM_SHA256, - TLS_DH_RSA_WITH_AES_256_GCM_SHA384, - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, - TLS_DH_DSS_WITH_AES_128_GCM_SHA256, - TLS_DH_DSS_WITH_AES_256_GCM_SHA384, - TLS_DH_anon_WITH_AES_128_GCM_SHA256, - TLS_DH_anon_WITH_AES_256_GCM_SHA384, - - /* RFC 5487 - PSK with SHA-256/384 and AES GCM */ - TLS_PSK_WITH_AES_128_GCM_SHA256, - TLS_PSK_WITH_AES_256_GCM_SHA384, - TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, - TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, - TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, - TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, - - TLS_PSK_WITH_AES_128_CBC_SHA256, - TLS_PSK_WITH_AES_256_CBC_SHA384, - TLS_PSK_WITH_NULL_SHA256, - TLS_PSK_WITH_NULL_SHA384, - - TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, - TLS_DHE_PSK_WITH_NULL_SHA256, - TLS_DHE_PSK_WITH_NULL_SHA384, - - TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, - TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, - TLS_RSA_PSK_WITH_NULL_SHA256, - TLS_RSA_PSK_WITH_NULL_SHA384, - - - /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with - HMAC SHA-256/384. */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, - - /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with - SHA-256/384 and AES Galois Counter Mode (GCM) */ - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, - - /* RFC 5746 - Secure Renegotiation */ - TLS_EMPTY_RENEGOTIATION_INFO_SCSV, - /* - * Tags for SSL 2 cipher kinds which are not specified - * for SSL 3. - */ - SSL_RSA_WITH_RC2_CBC_MD5, - SSL_RSA_WITH_IDEA_CBC_MD5, - SSL_RSA_WITH_DES_CBC_MD5, - SSL_RSA_WITH_3DES_EDE_CBC_MD5, - SSL_NO_SUCH_CIPHERSUITE -} diff --git a/vendor/security-framework/src/dlsym.rs b/vendor/security-framework/src/dlsym.rs deleted file mode 100644 index d3229298..00000000 --- a/vendor/security-framework/src/dlsym.rs +++ /dev/null @@ -1,50 +0,0 @@ -// dlsym.rs is taken from mio -// https://github.com/carllerche/mio/blob/master/src/sys/unix/dlsym.rs - -use std::marker; -use std::mem; -use std::sync::atomic::{AtomicUsize, Ordering}; - -use libc; - -macro_rules! dlsym { - (fn $name:ident($($t:ty),*) -> $ret:ty) => ( - #[allow(bad_style)] - static $name: $crate::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> = - $crate::dlsym::DlSym { - name: concat!(stringify!($name), "\0"), - addr: ::std::sync::atomic::AtomicUsize::new(0), - _marker: ::std::marker::PhantomData, - }; - ) -} - -pub struct DlSym<F> { - pub name: &'static str, - pub addr: AtomicUsize, - pub _marker: marker::PhantomData<F>, -} - -impl<F> DlSym<F> { - pub fn get(&self) -> Option<&F> { - assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>()); - unsafe { - if self.addr.load(Ordering::SeqCst) == 0 { - self.addr.store(fetch(self.name), Ordering::SeqCst); - } - if self.addr.load(Ordering::SeqCst) == 1 { - None - } else { - mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr) - } - } - } -} - -unsafe fn fetch(name: &str) -> usize { - assert_eq!(name.as_bytes()[name.len() - 1], 0); - match libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize { - 0 => 1, - n => n, - } -} diff --git a/vendor/security-framework/src/identity.rs b/vendor/security-framework/src/identity.rs deleted file mode 100644 index 33775251..00000000 --- a/vendor/security-framework/src/identity.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! Identity support. - -use core_foundation::base::TCFType; -use core_foundation::base::ToVoid; -use core_foundation::dictionary::CFMutableDictionary; -use security_framework_sys::base::SecIdentityRef; -use security_framework_sys::identity::{SecIdentityCopyCertificate, SecIdentityCopyPrivateKey, SecIdentityGetTypeID}; -use security_framework_sys::item::kSecValueRef; -use security_framework_sys::keychain_item::SecItemDelete; -use std::fmt; -use std::ptr; - -use crate::base::Error; -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::cvt; -use crate::key::SecKey; - -declare_TCFType! { - /// A type representing an identity. - /// - /// Identities are a certificate paired with the corresponding private key. - SecIdentity, SecIdentityRef -} -impl_TCFType!(SecIdentity, SecIdentityRef, SecIdentityGetTypeID); - -unsafe impl Sync for SecIdentity {} -unsafe impl Send for SecIdentity {} - -impl fmt::Debug for SecIdentity { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = fmt.debug_struct("SecIdentity"); - if let Ok(cert) = self.certificate() { - builder.field("certificate", &cert); - } - if let Ok(key) = self.private_key() { - builder.field("private_key", &key); - } - builder.finish() - } -} - -impl SecIdentity { - /// Returns the certificate corresponding to this identity. - pub fn certificate(&self) -> Result<SecCertificate> { - unsafe { - let mut certificate = ptr::null_mut(); - cvt(SecIdentityCopyCertificate(self.0, &mut certificate))?; - Ok(SecCertificate::wrap_under_create_rule(certificate)) - } - } - - /// Returns the private key corresponding to this identity. - pub fn private_key(&self) -> Result<SecKey> { - unsafe { - let mut key = ptr::null_mut(); - cvt(SecIdentityCopyPrivateKey(self.0, &mut key))?; - Ok(SecKey::wrap_under_create_rule(key)) - } - } - - /// Translates to `SecItemDelete`, passing in the `SecIdentityRef` - pub fn delete(&self) -> Result<(), Error> { - let query = CFMutableDictionary::from_CFType_pairs(&[( - unsafe { kSecValueRef }.to_void(), - self.to_void(), - )]); - - cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) }) - } -} - -#[cfg(test)] -mod test { - use super::SecIdentity; - - #[test] - fn identity_has_send_bound() { - fn assert_send<T: Send>() {} - assert_send::<SecIdentity>(); - } -} diff --git a/vendor/security-framework/src/import_export.rs b/vendor/security-framework/src/import_export.rs deleted file mode 100644 index 378a2d5b..00000000 --- a/vendor/security-framework/src/import_export.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Security Framework type import/export support. - -use core_foundation::array::CFArray; -use core_foundation::base::{CFType, TCFType}; -use core_foundation::data::CFData; -use core_foundation::dictionary::CFDictionary; -use core_foundation::string::CFString; -use security_framework_sys::import_export::*; -use std::ptr; - -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::cvt; -use crate::identity::SecIdentity; -#[cfg(target_os = "macos")] -use crate::os::macos::access::SecAccess; -#[cfg(target_os = "macos")] -use crate::os::macos::keychain::SecKeychain; -use crate::trust::SecTrust; - -/// Information about an imported identity. -pub struct ImportedIdentity { - /// The label of the identity. - pub label: Option<String>, - /// The ID of the identity. Typically the SHA-1 hash of the public key. - pub key_id: Option<Vec<u8>>, - /// A `SecTrust` object set up to validate this identity. - pub trust: Option<SecTrust>, - /// A certificate chain validating this identity. - pub cert_chain: Option<Vec<SecCertificate>>, - /// The identity itself. - pub identity: Option<SecIdentity>, - _p: (), -} - -/// A builder type to import an identity from PKCS#12 formatted data. -#[derive(Default)] -pub struct Pkcs12ImportOptions { - passphrase: Option<CFString>, - #[cfg(target_os = "macos")] - keychain: Option<SecKeychain>, - #[cfg(target_os = "macos")] - access: Option<SecAccess>, -} - -#[cfg(target_os = "macos")] -impl crate::Pkcs12ImportOptionsInternals for Pkcs12ImportOptions { - #[inline(always)] - fn keychain(&mut self, keychain: SecKeychain) -> &mut Self { - self.keychain = Some(keychain); - self - } - - #[inline(always)] - fn access(&mut self, access: SecAccess) -> &mut Self { - self.access = Some(access); - self - } -} - -impl Pkcs12ImportOptions { - /// Creates a new builder with default options. - #[inline(always)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Specifies the passphrase to be used to decrypt the data. - /// - /// This must be specified, as unencrypted PKCS#12 data is not supported. - #[inline] - pub fn passphrase(&mut self, passphrase: &str) -> &mut Self { - self.passphrase = Some(CFString::new(passphrase)); - self - } - - /// Imports identities from PKCS#12 encoded data. - pub fn import(&self, pkcs12_data: &[u8]) -> Result<Vec<ImportedIdentity>> { - unsafe { - let pkcs12_data = CFData::from_buffer(pkcs12_data); - - let mut options = vec![]; - - if let Some(ref passphrase) = self.passphrase { - options.push(( - CFString::wrap_under_get_rule(kSecImportExportPassphrase), - passphrase.as_CFType(), - )); - } - - self.import_setup(&mut options); - - let options = CFDictionary::from_CFType_pairs(&options); - - let mut raw_items = ptr::null(); - cvt(SecPKCS12Import( - pkcs12_data.as_concrete_TypeRef(), - options.as_concrete_TypeRef(), - &mut raw_items, - ))?; - let raw_items = CFArray::<CFDictionary<CFString, *const _>>::wrap_under_create_rule(raw_items); - - let mut items = vec![]; - - for raw_item in &raw_items { - let label = raw_item - .find(kSecImportItemLabel) - .map(|label| CFString::wrap_under_get_rule((*label).cast()).to_string()); - let key_id = raw_item - .find(kSecImportItemKeyID) - .map(|key_id| CFData::wrap_under_get_rule((*key_id).cast()).to_vec()); - let trust = raw_item - .find(kSecImportItemTrust) - .map(|trust| SecTrust::wrap_under_get_rule(*trust as *mut _)); - let cert_chain = raw_item.find(kSecImportItemCertChain.cast()).map( - |cert_chain| { - CFArray::<SecCertificate>::wrap_under_get_rule((*cert_chain).cast()) - .iter() - .map(|c| c.clone()) - .collect() - }, - ); - let identity = raw_item - .find(kSecImportItemIdentity) - .map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _)); - - items.push(ImportedIdentity { - label, - key_id, - trust, - cert_chain, - identity, - _p: (), - }); - } - - Ok(items) - } - } - - #[cfg(target_os = "macos")] - fn import_setup(&self, options: &mut Vec<(CFString, CFType)>) { - unsafe { - if let Some(ref keychain) = self.keychain { - options.push(( - CFString::wrap_under_get_rule(kSecImportExportKeychain), - keychain.as_CFType(), - )); - } - - if let Some(ref access) = self.access { - options.push(( - CFString::wrap_under_get_rule(kSecImportExportAccess), - access.as_CFType(), - )); - } - } - } - - #[cfg(not(target_os = "macos"))] - fn import_setup(&self, _: &mut Vec<(CFString, CFType)>) {} -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn missing_passphrase() { - let data = include_bytes!("../test/server.p12"); - assert!(Pkcs12ImportOptions::new().import(data).is_err()); - } -} diff --git a/vendor/security-framework/src/item.rs b/vendor/security-framework/src/item.rs deleted file mode 100644 index 55425ade..00000000 --- a/vendor/security-framework/src/item.rs +++ /dev/null @@ -1,793 +0,0 @@ -//! Support to search for items in a keychain. - -use core_foundation::array::CFArray; -use core_foundation::base::{CFType, TCFType, ToVoid}; -use core_foundation::boolean::CFBoolean; -use core_foundation::data::CFData; -use core_foundation::date::CFDate; -use core_foundation::dictionary::{CFDictionary, CFMutableDictionary}; -use core_foundation::number::CFNumber; -use core_foundation::string::CFString; -use core_foundation_sys::base::{CFCopyDescription, CFGetTypeID, CFRelease, CFTypeRef}; -use core_foundation_sys::string::CFStringRef; -use security_framework_sys::item::*; -use security_framework_sys::keychain_item::{SecItemAdd, SecItemCopyMatching}; -use std::collections::HashMap; -use std::fmt; -use std::ptr; - -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::cvt; -use crate::identity::SecIdentity; -use crate::key::SecKey; -#[cfg(target_os = "macos")] -use crate::os::macos::keychain::SecKeychain; - -/// Specifies the type of items to search for. -#[derive(Debug, Copy, Clone)] -pub struct ItemClass(CFStringRef); - -impl ItemClass { - /// Look for `SecKeychainItem`s corresponding to generic passwords. - #[inline(always)] - #[must_use] - pub fn generic_password() -> Self { - unsafe { Self(kSecClassGenericPassword) } - } - - /// Look for `SecKeychainItem`s corresponding to internet passwords. - #[inline(always)] - #[must_use] - pub fn internet_password() -> Self { - unsafe { Self(kSecClassInternetPassword) } - } - - /// Look for `SecCertificate`s. - #[inline(always)] - #[must_use] - pub fn certificate() -> Self { - unsafe { Self(kSecClassCertificate) } - } - - /// Look for `SecKey`s. - #[inline(always)] - #[must_use] - pub fn key() -> Self { - unsafe { Self(kSecClassKey) } - } - - /// Look for `SecIdentity`s. - #[inline(always)] - #[must_use] - pub fn identity() -> Self { - unsafe { Self(kSecClassIdentity) } - } - - #[inline] - fn to_value(self) -> CFType { - unsafe { CFType::wrap_under_get_rule(self.0.cast()) } - } -} - -/// Specifies the type of keys to search for. -#[derive(Debug, Copy, Clone)] -pub struct KeyClass(CFStringRef); - -impl KeyClass { - /// `kSecAttrKeyClassPublic` - #[inline(always)] - #[must_use] pub fn public() -> Self { - unsafe { Self(kSecAttrKeyClassPublic) } - } - /// `kSecAttrKeyClassPrivate` - #[inline(always)] - #[must_use] pub fn private() -> Self { - unsafe { Self(kSecAttrKeyClassPrivate) } - } - /// `kSecAttrKeyClassSymmetric` - #[inline(always)] - #[must_use] pub fn symmetric() -> Self { - unsafe { Self(kSecAttrKeyClassSymmetric) } - } - - #[inline] - fn to_value(self) -> CFType { - unsafe { CFType::wrap_under_get_rule(self.0.cast()) } - } -} - -/// Specifies the number of results returned by a search -#[derive(Debug, Copy, Clone)] -pub enum Limit { - /// Always return all results - All, - - /// Return up to the specified number of results - Max(i64), -} - -impl Limit { - #[inline] - fn to_value(self) -> CFType { - match self { - Self::All => unsafe { CFString::wrap_under_get_rule(kSecMatchLimitAll).into_CFType() }, - Self::Max(l) => CFNumber::from(l).into_CFType(), - } - } -} - -impl From<i64> for Limit { - #[inline] - fn from(limit: i64) -> Self { - Self::Max(limit) - } -} - -/// A builder type to search for items in keychains. -#[derive(Default)] -pub struct ItemSearchOptions { - #[cfg(target_os = "macos")] - keychains: Option<CFArray<SecKeychain>>, - #[cfg(not(target_os = "macos"))] - keychains: Option<CFArray<CFType>>, - case_insensitive: Option<bool>, - class: Option<ItemClass>, - key_class: Option<KeyClass>, - load_refs: bool, - load_attributes: bool, - load_data: bool, - limit: Option<Limit>, - trusted_only: Option<bool>, - label: Option<CFString>, - service: Option<CFString>, - subject: Option<CFString>, - account: Option<CFString>, - access_group: Option<CFString>, - pub_key_hash: Option<CFData>, - app_label: Option<CFData>, -} - -#[cfg(target_os = "macos")] -impl crate::ItemSearchOptionsInternals for ItemSearchOptions { - #[inline] - fn keychains(&mut self, keychains: &[SecKeychain]) -> &mut Self { - self.keychains = Some(CFArray::from_CFTypes(keychains)); - self - } -} - -impl ItemSearchOptions { - /// Creates a new builder with default options. - #[inline(always)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Search only for items of the specified class. - #[inline(always)] - pub fn class(&mut self, class: ItemClass) -> &mut Self { - self.class = Some(class); - self - } - - /// Whether search for an item should be case insensitive or not. - #[inline(always)] - pub fn case_insensitive(&mut self, case_insensitive: Option<bool>) -> &mut Self { - self.case_insensitive = case_insensitive; - self - } - - /// Search only for keys of the specified class. Also sets self.class to - /// `ItemClass::key()`. - #[inline(always)] - pub fn key_class(&mut self, key_class: KeyClass) -> &mut Self { - self.class(ItemClass::key()); - self.key_class = Some(key_class); - self - } - - /// Load Security Framework objects (`SecCertificate`, `SecKey`, etc) for - /// the results. - #[inline(always)] - pub fn load_refs(&mut self, load_refs: bool) -> &mut Self { - self.load_refs = load_refs; - self - } - - /// Load Security Framework object attributes for - /// the results. - #[inline(always)] - pub fn load_attributes(&mut self, load_attributes: bool) -> &mut Self { - self.load_attributes = load_attributes; - self - } - - /// Load Security Framework objects data for - /// the results. - #[inline(always)] - pub fn load_data(&mut self, load_data: bool) -> &mut Self { - self.load_data = load_data; - self - } - - /// Limit the number of search results. - /// - /// If this is not called, the default limit is 1. - #[inline(always)] - pub fn limit<T: Into<Limit>>(&mut self, limit: T) -> &mut Self { - self.limit = Some(limit.into()); - self - } - - /// Search for an item with the given label. - #[inline(always)] - pub fn label(&mut self, label: &str) -> &mut Self { - self.label = Some(CFString::new(label)); - self - } - - /// Whether untrusted certificates should be returned. - #[inline(always)] - pub fn trusted_only(&mut self, trusted_only: Option<bool>) -> &mut Self { - self.trusted_only = trusted_only; - self - } - - /// Search for an item with the given service. - #[inline(always)] - pub fn service(&mut self, service: &str) -> &mut Self { - self.service = Some(CFString::new(service)); - self - } - - /// Search for an item with exactly the given subject. - #[inline(always)] - pub fn subject(&mut self, subject: &str) -> &mut Self { - self.subject = Some(CFString::new(subject)); - self - } - - /// Search for an item with the given account. - #[inline(always)] - pub fn account(&mut self, account: &str) -> &mut Self { - self.account = Some(CFString::new(account)); - self - } - - /// Search for an item with a specific access group. - pub fn access_group(&mut self, access_group: &str) -> &mut Self { - self.access_group = Some(CFString::new(access_group)); - self - } - - /// Sets `kSecAttrAccessGroup` to `kSecAttrAccessGroupToken` - #[inline(always)] - pub fn access_group_token(&mut self) -> &mut Self { - self.access_group = unsafe { Some(CFString::wrap_under_get_rule(kSecAttrAccessGroupToken)) }; - self - } - - /// Search for a certificate with the given public key hash. - /// - /// This is only compatible with [`ItemClass::certificate`], to search for - /// a key by public key hash use [`ItemSearchOptions::application_label`] - /// instead. - #[inline(always)] - pub fn pub_key_hash(&mut self, pub_key_hash: &[u8]) -> &mut Self { - self.pub_key_hash = Some(CFData::from_buffer(pub_key_hash)); - self - } - - /// Search for a key with the given public key hash. - /// - /// This is only compatible with [`ItemClass::key`], to search for a - /// certificate by the public key hash use [`ItemSearchOptions::pub_key_hash`] - /// instead. - #[inline(always)] - pub fn application_label(&mut self, app_label: &[u8]) -> &mut Self { - self.app_label = Some(CFData::from_buffer(app_label)); - self - } - - /// Search for objects. - pub fn search(&self) -> Result<Vec<SearchResult>> { - unsafe { - let mut params = vec![]; - - if let Some(ref keychains) = self.keychains { - params.push(( - CFString::wrap_under_get_rule(kSecMatchSearchList), - keychains.as_CFType(), - )); - } - - if let Some(class) = self.class { - params.push((CFString::wrap_under_get_rule(kSecClass), class.to_value())); - } - - if let Some(case_insensitive) = self.case_insensitive { - params.push(( - CFString::wrap_under_get_rule(kSecMatchCaseInsensitive), - CFBoolean::from(case_insensitive).as_CFType() - )); - } - - if let Some(key_class) = self.key_class { - params.push((CFString::wrap_under_get_rule(kSecAttrKeyClass), key_class.to_value())); - } - - if self.load_refs { - params.push(( - CFString::wrap_under_get_rule(kSecReturnRef), - CFBoolean::true_value().into_CFType(), - )); - } - - if self.load_attributes { - params.push(( - CFString::wrap_under_get_rule(kSecReturnAttributes), - CFBoolean::true_value().into_CFType(), - )); - } - - if self.load_data { - params.push(( - CFString::wrap_under_get_rule(kSecReturnData), - CFBoolean::true_value().into_CFType(), - )); - } - - if let Some(limit) = self.limit { - params.push(( - CFString::wrap_under_get_rule(kSecMatchLimit), - limit.to_value(), - )); - } - - if let Some(ref label) = self.label { - params.push(( - CFString::wrap_under_get_rule(kSecAttrLabel), - label.as_CFType(), - )); - } - - if let Some(ref trusted_only) = self.trusted_only { - params.push(( - CFString::wrap_under_get_rule(kSecMatchTrustedOnly), - if *trusted_only { CFBoolean::true_value().into_CFType() } else { CFBoolean::false_value().into_CFType() }, - )); - } - - if let Some(ref service) = self.service { - params.push(( - CFString::wrap_under_get_rule(kSecAttrService), - service.as_CFType(), - )); - } - - #[cfg(target_os = "macos")] - { - if let Some(ref subject) = self.subject { - params.push(( - CFString::wrap_under_get_rule(kSecMatchSubjectWholeString), - subject.as_CFType(), - )); - } - } - - if let Some(ref account) = self.account { - params.push(( - CFString::wrap_under_get_rule(kSecAttrAccount), - account.as_CFType(), - )); - } - - if let Some(ref access_group) = self.access_group { - params.push(( - CFString::wrap_under_get_rule(kSecAttrAccessGroup), - access_group.as_CFType(), - )); - } - - if let Some(ref pub_key_hash) = self.pub_key_hash { - params.push(( - CFString::wrap_under_get_rule(kSecAttrPublicKeyHash), - pub_key_hash.as_CFType(), - )); - } - - if let Some(ref app_label) = self.app_label { - params.push(( - CFString::wrap_under_get_rule(kSecAttrApplicationLabel), - app_label.as_CFType(), - )); - } - - let params = CFDictionary::from_CFType_pairs(¶ms); - - let mut ret = ptr::null(); - cvt(SecItemCopyMatching(params.as_concrete_TypeRef(), &mut ret))?; - if ret.is_null() { - // SecItemCopyMatching returns NULL if no load_* was specified, - // causing a segfault. - return Ok(vec![]); - } - let type_id = CFGetTypeID(ret); - - let mut items = vec![]; - - if type_id == CFArray::<CFType>::type_id() { - let array: CFArray<CFType> = CFArray::wrap_under_create_rule(ret as *mut _); - for item in array.iter() { - items.push(get_item(item.as_CFTypeRef())); - } - } else { - items.push(get_item(ret)); - // This is a bit janky, but get_item uses wrap_under_get_rule - // which bumps the refcount but we want create semantics - CFRelease(ret); - } - - Ok(items) - } - } -} - -unsafe fn get_item(item: CFTypeRef) -> SearchResult { - let type_id = CFGetTypeID(item); - - if type_id == CFData::type_id() { - let data = CFData::wrap_under_get_rule(item as *mut _); - let mut buf = Vec::new(); - buf.extend_from_slice(data.bytes()); - return SearchResult::Data(buf); - } - - if type_id == CFDictionary::<*const u8, *const u8>::type_id() { - return SearchResult::Dict(CFDictionary::wrap_under_get_rule(item as *mut _)); - } - - #[cfg(target_os = "macos")] - { - use crate::os::macos::keychain_item::SecKeychainItem; - if type_id == SecKeychainItem::type_id() { - return SearchResult::Ref(Reference::KeychainItem( - SecKeychainItem::wrap_under_get_rule(item as *mut _), - )); - } - } - - let reference = if type_id == SecCertificate::type_id() { - Reference::Certificate(SecCertificate::wrap_under_get_rule(item as *mut _)) - } else if type_id == SecKey::type_id() { - Reference::Key(SecKey::wrap_under_get_rule(item as *mut _)) - } else if type_id == SecIdentity::type_id() { - Reference::Identity(SecIdentity::wrap_under_get_rule(item as *mut _)) - } else { - panic!("Got bad type from SecItemCopyMatching: {type_id}"); - }; - - SearchResult::Ref(reference) -} - -/// An enum including all objects whose references can be returned from a search. -/// Note that generic _Keychain Items_, such as passwords and preferences, do -/// not have specific object types; they are modeled using dictionaries and so -/// are available directly as search results in variant `SearchResult::Dict`. -#[derive(Debug)] -pub enum Reference { - /// A `SecIdentity`. - Identity(SecIdentity), - /// A `SecCertificate`. - Certificate(SecCertificate), - /// A `SecKey`. - Key(SecKey), - /// A `SecKeychainItem`. - /// - /// Only defined on OSX - #[cfg(target_os = "macos")] - KeychainItem(crate::os::macos::keychain_item::SecKeychainItem), - #[doc(hidden)] - __NonExhaustive, -} - -/// An individual search result. -pub enum SearchResult { - /// A reference to the Security Framework object, if asked for. - Ref(Reference), - /// A dictionary of data about the Security Framework object, if asked for. - Dict(CFDictionary), - /// The Security Framework object as bytes, if asked for. - Data(Vec<u8>), - /// An unknown representation of the Security Framework object. - Other, -} - -impl fmt::Debug for SearchResult { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::Ref(ref reference) => fmt - .debug_struct("SearchResult::Ref") - .field("reference", reference) - .finish(), - Self::Data(ref buf) => fmt - .debug_struct("SearchResult::Data") - .field("data", buf) - .finish(), - Self::Dict(_) => { - let mut debug = fmt.debug_struct("SearchResult::Dict"); - for (k, v) in self.simplify_dict().unwrap() { - debug.field(&k, &v); - } - debug.finish() - } - Self::Other => write!(fmt, "SearchResult::Other"), - } - } -} - -impl SearchResult { - /// If the search result is a `CFDict`, simplify that to a - /// `HashMap<String, String>`. This transformation isn't - /// comprehensive, it only supports `CFString`, `CFDate`, and `CFData` - /// value types. - #[must_use] - pub fn simplify_dict(&self) -> Option<HashMap<String, String>> { - match *self { - Self::Dict(ref d) => unsafe { - let mut retmap = HashMap::new(); - let (keys, values) = d.get_keys_and_values(); - for (k, v) in keys.iter().zip(values.iter()) { - let keycfstr = CFString::wrap_under_get_rule((*k).cast()); - let val: String = match CFGetTypeID(*v) { - cfstring if cfstring == CFString::type_id() => { - format!("{}", CFString::wrap_under_get_rule((*v).cast())) - } - cfdata if cfdata == CFData::type_id() => { - let buf = CFData::wrap_under_get_rule((*v).cast()); - let mut vec = Vec::new(); - vec.extend_from_slice(buf.bytes()); - format!("{}", String::from_utf8_lossy(&vec)) - } - cfdate if cfdate == CFDate::type_id() => format!( - "{}", - CFString::wrap_under_create_rule(CFCopyDescription(*v)) - ), - _ => String::from("unknown"), - }; - retmap.insert(format!("{keycfstr}"), val); - } - Some(retmap) - }, - _ => None, - } - } -} - -/// Builder-pattern struct for specifying options for `add_item` (`SecAddItem` -/// wrapper). -/// -/// When finished populating options, call `to_dictionary()` and pass the -/// resulting `CFDictionary` to `add_item`. -pub struct ItemAddOptions { - /// The value (by ref or data) of the item to add, required. - pub value: ItemAddValue, - /// Optional kSecAttrAccount attribute. - pub account_name: Option<CFString>, - /// Optional kSecAttrAccessGroup attribute. - pub access_group: Option<CFString>, - /// Optional kSecAttrComment attribute. - pub comment: Option<CFString>, - /// Optional kSecAttrDescription attribute. - pub description: Option<CFString>, - /// Optional kSecAttrLabel attribute. - pub label: Option<CFString>, - /// Optional kSecAttrService attribute. - pub service: Option<CFString>, - /// Optional keychain location. - pub location: Option<Location>, -} - -impl ItemAddOptions { - /// Specifies the item to add. - #[must_use] - pub fn new(value: ItemAddValue) -> Self { - Self{ value, label: None, location: None, service: None, account_name: None, comment: None, description: None, access_group: None } - } - - /// Specifies the `kSecAttrAccount` attribute. - pub fn set_account_name(&mut self, account_name: impl AsRef<str>) -> &mut Self { - self.account_name = Some(account_name.as_ref().into()); - self - } - /// Specifies the `kSecAttrAccessGroup` attribute. - pub fn set_access_group(&mut self, access_group: impl AsRef<str>) -> &mut Self { - self.access_group = Some(access_group.as_ref().into()); - self - } - /// Specifies the `kSecAttrComment` attribute. - pub fn set_comment(&mut self, comment: impl AsRef<str>) -> &mut Self { - self.comment = Some(comment.as_ref().into()); - self - } - /// Specifies the `kSecAttrDescription` attribute. - pub fn set_description(&mut self, description: impl AsRef<str>) -> &mut Self { - self.description = Some(description.as_ref().into()); - self - } - /// Specifies the `kSecAttrLabel` attribute. - pub fn set_label(&mut self, label: impl AsRef<str>) -> &mut Self { - self.label = Some(label.as_ref().into()); - self - } - /// Specifies which keychain to add the item to. - pub fn set_location(&mut self, location: Location) -> &mut Self { - self.location = Some(location); - self - } - /// Specifies the `kSecAttrService` attribute. - pub fn set_service(&mut self, service: impl AsRef<str>) -> &mut Self { - self.service = Some(service.as_ref().into()); - self - } - /// Populates a `CFDictionary` to be passed to - pub fn to_dictionary(&self) -> CFDictionary { - let mut dict = CFMutableDictionary::from_CFType_pairs(&[]); - - let class_opt = match &self.value { - ItemAddValue::Ref(ref_) => ref_.class(), - ItemAddValue::Data { class, .. } => Some(*class), - }; - if let Some(class) = class_opt { - dict.add(&unsafe { kSecClass }.to_void(), &class.0.to_void()); - } - - let value_pair = match &self.value { - ItemAddValue::Ref(ref_) => (unsafe { kSecValueRef }.to_void(), ref_.ref_()), - ItemAddValue::Data { data, .. } => (unsafe { kSecValueData }.to_void(), data.to_void()), - }; - dict.add(&value_pair.0, &value_pair.1); - - if let Some(location) = &self.location { - match location { - #[cfg(any(feature = "OSX_10_15", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - Location::DataProtectionKeychain => { - dict.add( - &unsafe { kSecUseDataProtectionKeychain }.to_void(), - &CFBoolean::true_value().to_void(), - ); - } - #[cfg(target_os = "macos")] - Location::DefaultFileKeychain => {} - #[cfg(target_os = "macos")] - Location::FileKeychain(keychain) => { - dict.add(&unsafe { kSecUseKeychain }.to_void(), &keychain.to_void()); - }, - } - } - if let Some(account_name) = &self.account_name { - dict.add(&unsafe { kSecAttrAccount }.to_void(), &account_name.to_void()); - } - if let Some(access_group) = &self.access_group { - dict.add(&unsafe { kSecAttrAccessGroup }.to_void(), &access_group.to_void()); - } - if let Some(comment) = &self.comment { - dict.add(&unsafe { kSecAttrComment }.to_void(), &comment.to_void()); - } - if let Some(description) = &self.description { - dict.add(&unsafe { kSecAttrDescription }.to_void(), &description.to_void()); - } - if let Some(label) = &self.label { - dict.add(&unsafe { kSecAttrLabel }.to_void(), &label.to_void()); - } - if let Some(service) = &self.service { - dict.add(&unsafe { kSecAttrService }.to_void(), &service.to_void()); - } - - dict.to_immutable() - } -} - -/// Value of an item to add to the keychain. -pub enum ItemAddValue { - /// Pass item by Ref (kSecValueRef) - Ref(AddRef), - /// Pass item by Data (kSecValueData) - Data { - /// The item class (kSecClass). - class: ItemClass, - /// The item data. - data: CFData, - }, -} - -/// Type of Ref to add to the keychain. -pub enum AddRef { - /// `SecKey` - Key(SecKey), - /// `SecIdentity` - Identity(SecIdentity), - /// `SecCertificate` - Certificate(SecCertificate), -} - -impl AddRef { - fn class(&self) -> Option<ItemClass> { - match self { - AddRef::Key(_) => Some(ItemClass::key()), - // kSecClass should not be specified when adding a SecIdentityRef: - // https://developer.apple.com/forums/thread/25751 - AddRef::Identity(_) => None, - AddRef::Certificate(_) => Some(ItemClass::certificate()), - } - } - fn ref_(&self) -> CFTypeRef { - match self { - AddRef::Key(key) => key.as_CFTypeRef(), - AddRef::Identity(id) => id.as_CFTypeRef(), - AddRef::Certificate(cert) => cert.as_CFTypeRef(), - } - } -} - -/// Which keychain to add an item to. -/// -/// <https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains> -pub enum Location { - /// Store the item in the newer DataProtectionKeychain. This is the only - /// keychain on iOS. On macOS, this is the newer and more consistent - /// keychain implementation. Keys stored in the Secure Enclave _must_ use - /// this keychain. - /// - /// This keychain requires the calling binary to be codesigned with - /// entitlements for the KeychainAccessGroups it is supposed to - /// access. - #[cfg(any(feature = "OSX_10_15", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - DataProtectionKeychain, - /// Store the key in the default file-based keychain. On macOS, defaults to - /// the Login keychain. - #[cfg(target_os = "macos")] - DefaultFileKeychain, - /// Store the key in a specific file-based keychain. - #[cfg(target_os = "macos")] - FileKeychain(crate::os::macos::keychain::SecKeychain), -} - -/// Translates to `SecItemAdd`. Use `ItemAddOptions` to build an `add_params` -/// `CFDictionary`. -pub fn add_item(add_params: CFDictionary) -> Result<()> { - cvt(unsafe { SecItemAdd(add_params.as_concrete_TypeRef(), std::ptr::null_mut()) }) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn find_nothing() { - assert!(ItemSearchOptions::new().search().is_err()); - } - - #[test] - fn limit_two() { - let results = ItemSearchOptions::new() - .class(ItemClass::certificate()) - .limit(2) - .search() - .unwrap(); - assert_eq!(results.len(), 2); - } - - #[test] - fn limit_all() { - let results = ItemSearchOptions::new() - .class(ItemClass::certificate()) - .limit(Limit::All) - .search() - .unwrap(); - assert!(results.len() >= 2); - } -} diff --git a/vendor/security-framework/src/key.rs b/vendor/security-framework/src/key.rs deleted file mode 100644 index a244de8c..00000000 --- a/vendor/security-framework/src/key.rs +++ /dev/null @@ -1,398 +0,0 @@ -//! Encryption key support - -use crate::cvt; -use core_foundation::{ - base::TCFType, string::{CFStringRef, CFString}, - dictionary::CFMutableDictionary, -}; -use core_foundation::base::ToVoid; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::boolean::CFBoolean; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::data::CFData; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::dictionary::CFDictionary; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::number::CFNumber; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use core_foundation::error::{CFError, CFErrorRef}; - -use security_framework_sys::{ - item::{kSecAttrKeyTypeRSA, kSecValueRef}, - keychain_item::SecItemDelete -}; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use security_framework_sys::{item::{ - kSecAttrIsPermanent, kSecAttrLabel, kSecAttrKeyType, - kSecAttrKeySizeInBits, kSecPrivateKeyAttrs, kSecAttrAccessControl -}}; -#[cfg(target_os="macos")] -use security_framework_sys::item::{ - kSecAttrKeyType3DES, kSecAttrKeyTypeDSA, kSecAttrKeyTypeAES, - kSecAttrKeyTypeDES, kSecAttrKeyTypeRC4, kSecAttrKeyTypeCAST, -}; - -use security_framework_sys::key::SecKeyGetTypeID; -use security_framework_sys::base::SecKeyRef; - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub use security_framework_sys::key::Algorithm; - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use security_framework_sys::key::{ - SecKeyCopyAttributes, SecKeyCopyExternalRepresentation, - SecKeyCreateSignature, SecKeyCreateRandomKey, - SecKeyCopyPublicKey, -}; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use security_framework_sys::item::kSecAttrApplicationLabel; -use std::fmt; - - -use crate::base::Error; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use crate::item::Location; -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -use crate::access_control::SecAccessControl; -/// Types of `SecKey`s. -#[derive(Debug, Copy, Clone)] -pub struct KeyType(CFStringRef); - -#[allow(missing_docs)] -impl KeyType { - #[inline(always)] - #[must_use] - pub fn rsa() -> Self { - unsafe { Self(kSecAttrKeyTypeRSA) } - } - - #[cfg(target_os = "macos")] - #[inline(always)] - #[must_use] - pub fn dsa() -> Self { - unsafe { Self(kSecAttrKeyTypeDSA) } - } - - #[cfg(target_os = "macos")] - #[inline(always)] - #[must_use] - pub fn aes() -> Self { - unsafe { Self(kSecAttrKeyTypeAES) } - } - - #[cfg(target_os = "macos")] - #[inline(always)] - #[must_use] - pub fn des() -> Self { - unsafe { Self(kSecAttrKeyTypeDES) } - } - - #[cfg(target_os = "macos")] - #[inline(always)] - #[must_use] - pub fn triple_des() -> Self { - unsafe { Self(kSecAttrKeyType3DES) } - } - - #[cfg(target_os = "macos")] - #[inline(always)] - #[must_use] - pub fn rc4() -> Self { - unsafe { Self(kSecAttrKeyTypeRC4) } - } - - #[cfg(target_os = "macos")] - #[inline(always)] - #[must_use] - pub fn cast() -> Self { - unsafe { Self(kSecAttrKeyTypeCAST) } - } - - #[inline(always)] - #[must_use] - pub fn ec() -> Self { - use security_framework_sys::item::kSecAttrKeyTypeEC; - - unsafe { Self(kSecAttrKeyTypeEC) } - } - - pub(crate) fn to_str(self) -> CFString { - unsafe { CFString::wrap_under_get_rule(self.0) } - } -} - -declare_TCFType! { - /// A type representing an encryption key. - SecKey, SecKeyRef -} -impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID); - -unsafe impl Sync for SecKey {} -unsafe impl Send for SecKey {} - -impl SecKey { - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Translates to `SecKeyCreateRandomKey` - /// `GenerateKeyOptions` provides a helper to create an attribute - /// `CFDictionary`. - pub fn generate(attributes: CFDictionary) -> Result<Self, CFError> { - let mut error: CFErrorRef = ::std::ptr::null_mut(); - let sec_key = unsafe { SecKeyCreateRandomKey(attributes.as_concrete_TypeRef(), &mut error)}; - if !error.is_null() { - Err(unsafe { CFError::wrap_under_create_rule(error) }) - } else { - Ok(unsafe { SecKey::wrap_under_create_rule(sec_key) }) - } - } - - /// Returns the programmatic identifier for the key. For keys of class - /// kSecAttrKeyClassPublic and kSecAttrKeyClassPrivate, the value is the - /// hash of the public key. - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - pub fn application_label(&self) -> Option<Vec<u8>> { - self.attributes() - .find(unsafe { kSecAttrApplicationLabel.to_void() }) - .map(|v| unsafe { CFData::wrap_under_get_rule(v.cast()) }.to_vec()) - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Translates to `SecKeyCopyAttributes` - #[must_use] - pub fn attributes(&self) -> CFDictionary { - let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) }; - unsafe { CFDictionary::wrap_under_create_rule(pka) } - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Translates to `SecKeyCopyExternalRepresentation` - #[must_use] - pub fn external_representation(&self) -> Option<CFData> { - let mut error: CFErrorRef = ::std::ptr::null_mut(); - let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) }; - if data.is_null() { - return None; - } - Some(unsafe { CFData::wrap_under_create_rule(data) }) - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Translates to `SecKeyCopyPublicKey` - #[must_use] - pub fn public_key(&self) -> Option<Self> { - let pub_seckey = unsafe { SecKeyCopyPublicKey(self.0.cast()) }; - if pub_seckey.is_null() { - return None; - } - - Some(unsafe { SecKey::wrap_under_create_rule(pub_seckey) }) - } - - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - /// Creates the cryptographic signature for a block of data using a private - /// key and specified algorithm. - pub fn create_signature(&self, algorithm: Algorithm, input: &[u8]) -> Result<Vec<u8>, CFError> { - let mut error: CFErrorRef = std::ptr::null_mut(); - - let output = unsafe { - SecKeyCreateSignature( - self.as_concrete_TypeRef(), - algorithm.into(), - CFData::from_buffer(input).as_concrete_TypeRef(), - &mut error, - ) - }; - - if !error.is_null() { - Err(unsafe { CFError::wrap_under_create_rule(error) }) - } else { - let output = unsafe { CFData::wrap_under_create_rule(output) }; - Ok(output.to_vec()) - } - } - - /// Verifies the cryptographic signature for a block of data using a public - /// key and specified algorithm. - #[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - pub fn verify_signature(&self, algorithm: Algorithm, signed_data: &[u8], signature: &[u8]) -> Result<bool, CFError> { - use security_framework_sys::key::SecKeyVerifySignature; - let mut error: CFErrorRef = std::ptr::null_mut(); - - let valid = unsafe { - SecKeyVerifySignature( - self.as_concrete_TypeRef(), - algorithm.into(), - CFData::from_buffer(signed_data).as_concrete_TypeRef(), - CFData::from_buffer(signature).as_concrete_TypeRef(), - &mut error, - ) - }; - - if !error.is_null() { - return Err(unsafe { CFError::wrap_under_create_rule(error) })?; - } - Ok(valid != 0) - } - - /// Translates to `SecItemDelete`, passing in the `SecKeyRef` - pub fn delete(&self) -> Result<(), Error> { - let query = CFMutableDictionary::from_CFType_pairs(&[( - unsafe { kSecValueRef }.to_void(), - self.to_void(), - )]); - - cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) }) - } -} - -/// Where to generate the key. -pub enum Token { - /// Generate the key in software, compatible with all `KeyType`s. - Software, - /// Generate the key in the Secure Enclave such that the private key is not - /// extractable. Only compatible with `KeyType::ec()`. - SecureEnclave, -} - -/// Helper for creating `CFDictionary` attributes for `SecKey::generate` -/// Recommended reading: -/// <https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains> -#[derive(Default)] -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub struct GenerateKeyOptions { - /// kSecAttrKeyType - pub key_type: Option<KeyType>, - /// kSecAttrKeySizeInBits - pub size_in_bits: Option<u32>, - /// kSecAttrLabel - pub label: Option<String>, - /// kSecAttrTokenID - pub token: Option<Token>, - /// Which keychain to store the key in, if any. - pub location: Option<Location>, - /// Access control - pub access_control: Option<SecAccessControl>, -} - -#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -impl GenerateKeyOptions { - /// Set `key_type` - pub fn set_key_type(&mut self, key_type: KeyType) -> &mut Self { - self.key_type = Some(key_type); - self - } - /// Set `size_in_bits` - pub fn set_size_in_bits(&mut self, size_in_bits: u32) -> &mut Self { - self.size_in_bits = Some(size_in_bits); - self - } - /// Set `label` - pub fn set_label(&mut self, label: impl Into<String>) -> &mut Self { - self.label = Some(label.into()); - self - } - /// Set `token` - pub fn set_token(&mut self, token: Token) -> &mut Self { - self.token = Some(token); - self - } - /// Set `location` - pub fn set_location(&mut self, location: Location) -> &mut Self { - self.location = Some(location); - self - } - /// Set `access_control` - pub fn set_access_control(&mut self, access_control: SecAccessControl) -> &mut Self { - self.access_control = Some(access_control); - self - } - - /// Collect options into a `CFDictioanry` - pub fn to_dictionary(&self) -> CFDictionary { - #[cfg(target_os = "macos")] - use security_framework_sys::item::kSecUseKeychain; - use security_framework_sys::item::{ - kSecAttrTokenID, kSecAttrTokenIDSecureEnclave, kSecPublicKeyAttrs, - }; - - let is_permanent = CFBoolean::from(self.location.is_some()); - let mut private_attributes = CFMutableDictionary::from_CFType_pairs(&[( - unsafe { kSecAttrIsPermanent }.to_void(), - is_permanent.to_void(), - )]); - if let Some(access_control) = &self.access_control { - private_attributes.set(unsafe { kSecAttrAccessControl }.to_void(), access_control.to_void()); - } - - let public_attributes = CFMutableDictionary::from_CFType_pairs(&[( - unsafe { kSecAttrIsPermanent }.to_void(), - is_permanent.to_void(), - )]); - - let key_type = self.key_type.unwrap_or_else(KeyType::rsa).to_str(); - - let size_in_bits = self.size_in_bits.unwrap_or(match () { - _ if key_type == KeyType::rsa().to_str() => 2048, - _ if key_type == KeyType::ec().to_str() => 256, - _ => 256, - }); - let size_in_bits = CFNumber::from(size_in_bits as i32); - - let mut attribute_key_values = vec![ - (unsafe { kSecAttrKeyType }.to_void(), key_type.to_void()), - ( - unsafe { kSecAttrKeySizeInBits }.to_void(), - size_in_bits.to_void(), - ), - ( - unsafe { kSecPrivateKeyAttrs }.to_void(), - private_attributes.to_void(), - ), - ( - unsafe { kSecPublicKeyAttrs }.to_void(), - public_attributes.to_void(), - ), - ]; - let label = self.label.as_deref().map(CFString::new); - if let Some(label) = &label { - attribute_key_values.push((unsafe { kSecAttrLabel }.to_void(), label.to_void())); - } - - #[cfg(target_os = "macos")] - match &self.location { - #[cfg(feature = "OSX_10_15")] - Some(Location::DataProtectionKeychain) => { - use security_framework_sys::item::kSecUseDataProtectionKeychain; - attribute_key_values.push(( - unsafe { kSecUseDataProtectionKeychain }.to_void(), - CFBoolean::true_value().to_void(), - )); - } - Some(Location::FileKeychain(keychain)) => { - attribute_key_values.push(( - unsafe { kSecUseKeychain }.to_void(), - keychain.as_concrete_TypeRef().to_void(), - )); - } - _ => {} - } - - match self.token.as_ref().unwrap_or(&Token::Software) { - Token::Software => {}, - Token::SecureEnclave => { - attribute_key_values.push(( - unsafe { kSecAttrTokenID }.to_void(), - unsafe { kSecAttrTokenIDSecureEnclave }.to_void(), - )); - } - } - - CFMutableDictionary::from_CFType_pairs(&attribute_key_values).to_immutable() - } -} - -impl fmt::Debug for SecKey { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("SecKey").finish_non_exhaustive() - } -} diff --git a/vendor/security-framework/src/lib.rs b/vendor/security-framework/src/lib.rs deleted file mode 100644 index 81631afe..00000000 --- a/vendor/security-framework/src/lib.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! Wrappers around the OSX Security Framework. -#![warn(missing_docs)] -#![allow(non_upper_case_globals)] -#![allow(clippy::manual_non_exhaustive)] // MSRV -#![allow(clippy::bad_bit_mask)] // bitflags - -#[macro_use] -extern crate core_foundation; - -use core_foundation_sys::base::OSStatus; -use security_framework_sys::base::errSecSuccess; - -use crate::base::{Error, Result}; -#[cfg(target_os = "macos")] -use crate::os::macos::access::SecAccess; -#[cfg(target_os = "macos")] -use crate::os::macos::keychain::SecKeychain; - -#[cfg(test)] -macro_rules! p { - ($e:expr) => { - match $e { - Ok(s) => s, - Err(e) => panic!("{:?}", e), - } - }; -} - -#[cfg(all(not(feature = "OSX_10_13"), any(feature = "alpn", feature = "session-tickets")))] -#[macro_use] -mod dlsym; - -pub mod access_control; -#[cfg(target_os = "macos")] -pub mod authorization; -pub mod base; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod certificate; -pub mod cipher_suite; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod identity; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod import_export; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod item; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod key; -pub mod os; -pub mod passwords; -pub mod passwords_options; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod policy; -pub mod random; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod secure_transport; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] -pub mod trust; -#[cfg(target_os = "macos")] -pub mod trust_settings; - -#[cfg(target_os = "macos")] -trait Pkcs12ImportOptionsInternals { - fn keychain(&mut self, keychain: SecKeychain) -> &mut Self; - fn access(&mut self, access: SecAccess) -> &mut Self; -} - -#[cfg(target_os = "macos")] -trait ItemSearchOptionsInternals { - fn keychains(&mut self, keychains: &[SecKeychain]) -> &mut Self; -} - -trait AsInner { - type Inner; - fn as_inner(&self) -> Self::Inner; -} - -#[inline(always)] -fn cvt(err: OSStatus) -> Result<()> { - match err { - errSecSuccess => Ok(()), - err => Err(Error::from_code(err)), - } -} - -#[cfg(test)] -mod test { - use crate::certificate::SecCertificate; - - pub fn certificate() -> SecCertificate { - let certificate = include_bytes!("../test/server.der"); - p!(SecCertificate::from_der(certificate)) - } -} diff --git a/vendor/security-framework/src/os/macos/access.rs b/vendor/security-framework/src/os/macos/access.rs deleted file mode 100644 index 1c41d85d..00000000 --- a/vendor/security-framework/src/os/macos/access.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Access functionality. - -use core_foundation::base::TCFType; -use security_framework_sys::access::SecAccessGetTypeID; -use security_framework_sys::base::SecAccessRef; - -declare_TCFType! { - /// A type representing access settings. - SecAccess, SecAccessRef -} -impl_TCFType!(SecAccess, SecAccessRef, SecAccessGetTypeID); - -unsafe impl Sync for SecAccess {} -unsafe impl Send for SecAccess {} diff --git a/vendor/security-framework/src/os/macos/certificate.rs b/vendor/security-framework/src/os/macos/certificate.rs deleted file mode 100644 index b50ee52d..00000000 --- a/vendor/security-framework/src/os/macos/certificate.rs +++ /dev/null @@ -1,267 +0,0 @@ -//! OSX specific extensions to certificate functionality. - -use core_foundation::array::{CFArray, CFArrayIterator}; -use core_foundation::base::TCFType; -use core_foundation::base::ToVoid; -use core_foundation::data::CFData; -use core_foundation::dictionary::CFDictionary; -use core_foundation::error::CFError; -use core_foundation::string::CFString; -use security_framework_sys::certificate::*; -use std::os::raw::c_void; -use std::ptr; - -use crate::base::Error; -use crate::certificate::SecCertificate; -use crate::cvt; -use crate::key::SecKey; -use crate::os::macos::certificate_oids::CertificateOid; -use crate::os::macos::digest_transform::{Builder, DigestType}; - -/// An extension trait adding OSX specific functionality to `SecCertificate`. -pub trait SecCertificateExt { - /// Returns the common name associated with the certificate. - fn common_name(&self) -> Result<String, Error>; - - /// Returns the public key associated with the certificate. - #[cfg_attr(not(feature = "OSX_10_14"), deprecated(note = "Uses deprecated SecCertificateCopyPublicKey. Enable OSX_10_14 feature to avoid it"))] - fn public_key(&self) -> Result<SecKey, Error>; - - /// Returns the set of properties associated with the certificate. - /// - /// The `keys` argument can optionally be used to filter the properties loaded to an explicit - /// subset. - fn properties(&self, keys: Option<&[CertificateOid]>) - -> Result<CertificateProperties, CFError>; - - /// Returns the SHA-256 fingerprint of the certificate. - fn fingerprint(&self) -> Result<[u8; 32], CFError> { unimplemented!() } -} - -impl SecCertificateExt for SecCertificate { - fn common_name(&self) -> Result<String, Error> { - unsafe { - let mut string = ptr::null(); - cvt(SecCertificateCopyCommonName( - self.as_concrete_TypeRef(), - &mut string, - ))?; - Ok(CFString::wrap_under_create_rule(string).to_string()) - } - } - - #[cfg(feature = "OSX_10_14")] - fn public_key(&self) -> Result<SecKey, Error> { - unsafe { - let key = SecCertificateCopyKey(self.as_concrete_TypeRef()); - if key.is_null() { - return Err(Error::from_code(-26275)); - } - Ok(SecKey::wrap_under_create_rule(key)) - } - } - - #[cfg(not(feature = "OSX_10_14"))] - fn public_key(&self) -> Result<SecKey, Error> { - #[allow(deprecated)] - unsafe { - let mut key = ptr::null_mut(); - cvt(SecCertificateCopyPublicKey( - self.as_concrete_TypeRef(), - &mut key, - ))?; - Ok(SecKey::wrap_under_create_rule(key)) - } - } - - fn properties( - &self, - keys: Option<&[CertificateOid]>, - ) -> Result<CertificateProperties, CFError> { - unsafe { - let keys = keys.map(|oids| { - let oids = oids.iter().map(|oid| oid.to_str()).collect::<Vec<_>>(); - CFArray::from_CFTypes(&oids) - }); - - let keys = match keys { - Some(ref keys) => keys.as_concrete_TypeRef(), - None => ptr::null_mut(), - }; - - let mut error = ptr::null_mut(); - - let dictionary = SecCertificateCopyValues(self.as_concrete_TypeRef(), keys, &mut error); - - if error.is_null() { - Ok(CertificateProperties(CFDictionary::wrap_under_create_rule( - dictionary, - ))) - } else { - Err(CFError::wrap_under_create_rule(error)) - } - } - } - - /// Returns the SHA-256 fingerprint of the certificate. - fn fingerprint(&self) -> Result<[u8; 32], CFError> { - let data = CFData::from_buffer(&self.to_der()); - let hash = Builder::new() - .type_(DigestType::sha2()) - .length(256) - .execute(&data)?; - Ok(hash.bytes().try_into().unwrap()) - } -} - -/// Properties associated with a certificate. -pub struct CertificateProperties(CFDictionary); - -impl CertificateProperties { - /// Retrieves a specific property identified by its OID. - #[must_use] pub fn get(&self, oid: CertificateOid) -> Option<CertificateProperty> { - unsafe { - self.0.find(oid.as_ptr().cast::<c_void>()).map(|value| { - CertificateProperty(CFDictionary::wrap_under_get_rule(*value as *mut _)) - }) - } - } -} - -/// A property associated with a certificate. -pub struct CertificateProperty(CFDictionary); - -impl CertificateProperty { - /// Returns the label of this property. - #[must_use] - pub fn label(&self) -> CFString { - unsafe { - CFString::wrap_under_get_rule((*self.0.get(kSecPropertyKeyLabel.to_void())).cast()) - } - } - - /// Returns an enum of the underlying data for this property. - #[must_use] - pub fn get(&self) -> PropertyType { - unsafe { - let type_ = - CFString::wrap_under_get_rule(*self.0.get(kSecPropertyKeyType.to_void()) as *mut _); - let value = self.0.get(kSecPropertyKeyValue.to_void()); - - if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeSection) { - PropertyType::Section(PropertySection(CFArray::wrap_under_get_rule( - (*value).cast(), - ))) - } else if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeString) { - PropertyType::String(CFString::wrap_under_get_rule((*value).cast())) - } else { - PropertyType::__Unknown - } - } - } -} - -/// A "section" property. -/// -/// Sections are sequences of other properties. -pub struct PropertySection(CFArray<CFDictionary>); - -impl PropertySection { - /// Returns an iterator over the properties in this section. - #[inline(always)] - #[must_use] - pub fn iter(&self) -> PropertySectionIter<'_> { - PropertySectionIter(self.0.iter()) - } -} - -impl<'a> IntoIterator for &'a PropertySection { - type IntoIter = PropertySectionIter<'a>; - type Item = CertificateProperty; - - #[inline(always)] - fn into_iter(self) -> PropertySectionIter<'a> { - self.iter() - } -} - -/// An iterator over the properties in a section. -pub struct PropertySectionIter<'a>(CFArrayIterator<'a, CFDictionary>); - -impl<'a> Iterator for PropertySectionIter<'a> { - type Item = CertificateProperty; - - #[inline] - fn next(&mut self) -> Option<CertificateProperty> { - self.0.next().map(|t| CertificateProperty(t.clone())) - } - - #[inline(always)] - fn size_hint(&self) -> (usize, Option<usize>) { - self.0.size_hint() - } -} - -/// An enum of the various types of properties. -pub enum PropertyType { - /// A section. - Section(PropertySection), - /// A string. - String(CFString), - #[doc(hidden)] - __Unknown, -} - -#[cfg(test)] -mod test { - use super::*; - use crate::test::certificate; - use std::collections::HashMap; - - #[test] - fn common_name() { - let certificate = certificate(); - assert_eq!("foobar.com", p!(certificate.common_name())); - } - - #[test] - #[allow(deprecated)] - fn public_key() { - let certificate = certificate(); - p!(certificate.public_key()); - } - - #[test] - fn fingerprint() { - let certificate = certificate(); - let fingerprint = p!(certificate.fingerprint()); - assert_eq!( - "af9dd180a326ae08b37e6398f9262f8b9d4c55674a233a7c84975024f873655d", - hex::encode(fingerprint) - ); - } - - #[test] - fn signature_algorithm() { - let certificate = certificate(); - let properties = certificate - .properties(Some(&[CertificateOid::x509_v1_signature_algorithm()])) - .unwrap(); - let value = properties - .get(CertificateOid::x509_v1_signature_algorithm()) - .unwrap(); - let section = match value.get() { - PropertyType::Section(section) => section, - _ => panic!(), - }; - let properties = section - .iter() - .map(|p| (p.label().to_string(), p.get())) - .collect::<HashMap<_, _>>(); - let algorithm = match properties["Algorithm"] { - PropertyType::String(ref s) => s.to_string(), - _ => panic!(), - }; - assert_eq!(algorithm, "1.2.840.113549.1.1.5"); - } -} diff --git a/vendor/security-framework/src/os/macos/certificate_oids.rs b/vendor/security-framework/src/os/macos/certificate_oids.rs deleted file mode 100644 index d820afe4..00000000 --- a/vendor/security-framework/src/os/macos/certificate_oids.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! OIDs associated with certificate properties. -use core_foundation::base::TCFType; -use core_foundation::string::CFString; -use core_foundation_sys::string::CFStringRef; -use security_framework_sys::certificate_oids::kSecOIDX509V1SignatureAlgorithm; - -/// An identifier of a property of a certificate. -#[derive(Copy, Clone)] -pub struct CertificateOid(CFStringRef); - -#[allow(missing_docs)] -impl CertificateOid { - #[inline(always)] - #[must_use] - pub fn x509_v1_signature_algorithm() -> Self { - unsafe { Self(kSecOIDX509V1SignatureAlgorithm) } - } - - /// Returns the underlying raw pointer corresponding to this OID. - #[inline(always)] - #[must_use] - pub fn as_ptr(&self) -> CFStringRef { - self.0 - } - - /// Returns the string representation of the OID. - #[inline] - #[must_use] - pub fn to_str(&self) -> CFString { - unsafe { CFString::wrap_under_get_rule(self.0) } - } -} diff --git a/vendor/security-framework/src/os/macos/code_signing.rs b/vendor/security-framework/src/os/macos/code_signing.rs deleted file mode 100644 index b42917c1..00000000 --- a/vendor/security-framework/src/os/macos/code_signing.rs +++ /dev/null @@ -1,486 +0,0 @@ -//! Code signing services. - -use std::{fmt::Debug, mem::MaybeUninit, str::FromStr}; - -use core_foundation::{ - base::{TCFType, TCFTypeRef, ToVoid}, - data::CFDataRef, - dictionary::CFMutableDictionary, - number::CFNumber, - string::{CFString, CFStringRef}, - url::CFURL, -}; -use libc::pid_t; -use security_framework_sys::code_signing::{ - kSecCSBasicValidateOnly, kSecCSCheckAllArchitectures, kSecCSCheckGatekeeperArchitectures, - kSecCSCheckNestedCode, kSecCSCheckTrustedAnchors, kSecCSConsiderExpiration, - kSecCSDoNotValidateExecutable, kSecCSDoNotValidateResources, kSecCSEnforceRevocationChecks, - kSecCSFullReport, kSecCSNoNetworkAccess, kSecCSQuickCheck, kSecCSReportProgress, - kSecCSRestrictSidebandData, kSecCSRestrictSymlinks, kSecCSRestrictToAppLike, - kSecCSSingleThreaded, kSecCSStrictValidate, kSecCSUseSoftwareSigningCert, kSecCSValidatePEH, - kSecGuestAttributeAudit, kSecGuestAttributePid, SecCodeCheckValidity, - SecCodeCopyGuestWithAttributes, SecCodeCopyPath, SecCodeCopySelf, SecCodeGetTypeID, SecCodeRef, - SecRequirementCreateWithString, SecRequirementGetTypeID, SecRequirementRef, - SecStaticCodeCheckValidity, SecStaticCodeCreateWithPath, SecStaticCodeGetTypeID, - SecStaticCodeRef, -}; - -use crate::{cvt, Result}; - -bitflags::bitflags! { - - /// Values that can be used in the flags parameter to most code signing - /// functions. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct Flags: u32 { - /// Use the default behaviour. - const NONE = 0; - - /// For multi-architecture (universal) Mach-O programs, validate all - /// architectures included. - const CHECK_ALL_ARCHITECTURES = kSecCSCheckAllArchitectures; - - /// Do not validate the contents of the main executable. - const DO_NOT_VALIDATE_EXECUTABLE = kSecCSDoNotValidateExecutable; - - /// Do not validate the presence and contents of all bundle resources - /// if any. - const DO_NOT_VALIDATE_RESOURCES = kSecCSDoNotValidateResources; - - /// Do not validate either the main executable or the bundle resources, - /// if any. - const BASIC_VALIDATE_ONLY = kSecCSBasicValidateOnly; - - /// For code in bundle form, locate and recursively check embedded code. - const CHECK_NESTED_CODE = kSecCSCheckNestedCode; - - /// Perform additional checks to ensure the validity of code in bundle - /// form. - const STRICT_VALIDATE = kSecCSStrictValidate; - - /// Apple have not documented this flag. - const FULL_REPORT = kSecCSFullReport; - - /// Apple have not documented this flag. - const CHECK_GATEKEEPER_ARCHITECTURES = kSecCSCheckGatekeeperArchitectures; - - /// Apple have not documented this flag. - const RESTRICT_SYMLINKS = kSecCSRestrictSymlinks; - - /// Apple have not documented this flag. - const RESTRICT_TO_APP_LIKE = kSecCSRestrictToAppLike; - - /// Apple have not documented this flag. - const RESTRICT_SIDEBAND_DATA = kSecCSRestrictSidebandData; - - /// Apple have not documented this flag. - const USE_SOFTWARE_SIGNING_CERT = kSecCSUseSoftwareSigningCert; - - /// Apple have not documented this flag. - const VALIDATE_PEH = kSecCSValidatePEH; - - /// Apple have not documented this flag. - const SINGLE_THREADED = kSecCSSingleThreaded; - - /// Apple have not documented this flag. - const QUICK_CHECK = kSecCSQuickCheck; - - /// Apple have not documented this flag. - const CHECK_TRUSTED_ANCHORS = kSecCSCheckTrustedAnchors; - - /// Apple have not documented this flag. - const REPORT_PROGRESS = kSecCSReportProgress; - - /// Apple have not documented this flag. - const NO_NETWORK_ACCESS = kSecCSNoNetworkAccess; - - /// Apple have not documented this flag. - const ENFORCE_REVOCATION_CHECKS = kSecCSEnforceRevocationChecks; - - /// Apple have not documented this flag. - const CONSIDER_EXPIRATION = kSecCSConsiderExpiration; - } -} - -impl Default for Flags { - #[inline(always)] - fn default() -> Self { - Self::NONE - } -} - -/// A helper to create guest attributes, which are normally passed as a -/// `CFDictionary` with varying types. -pub struct GuestAttributes { - inner: CFMutableDictionary, -} - -impl GuestAttributes { - // Not implemented: - // - architecture - // - canonical - // - dynamic code - // - dynamic code info plist - // - hash - // - mach port - // - sub-architecture - - /// Creates a new, empty `GuestAttributes`. You must add values to it in - /// order for it to be of any use. - #[must_use] - pub fn new() -> Self { - Self { - inner: CFMutableDictionary::new(), - } - } - - /// The guest's audit token. - pub fn set_audit_token(&mut self, token: CFDataRef) { - let key = unsafe { CFString::wrap_under_get_rule(kSecGuestAttributeAudit) }; - self.inner.add(&key.as_CFTypeRef(), &token.to_void()); - } - - /// The guest's pid. - pub fn set_pid(&mut self, pid: pid_t) { - let key = unsafe { CFString::wrap_under_get_rule(kSecGuestAttributePid) }; - let pid = CFNumber::from(pid); - self.inner.add(&key.as_CFTypeRef(), &pid.as_CFTypeRef()); - } - - /// Support for arbirtary guest attributes. - pub fn set_other<V: ToVoid<V>>(&mut self, key: CFStringRef, value: V) { - self.inner.add(&key.as_void_ptr(), &value.to_void()); - } -} - -impl Default for GuestAttributes { - fn default() -> Self { - Self::new() - } -} - -declare_TCFType! { - /// A code object representing signed code running on the system. - SecRequirement, SecRequirementRef -} -impl_TCFType!(SecRequirement, SecRequirementRef, SecRequirementGetTypeID); - -impl FromStr for SecRequirement { - type Err = crate::base::Error; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - let text = CFString::new(s); - let mut requirement = MaybeUninit::uninit(); - - unsafe { - cvt(SecRequirementCreateWithString( - text.as_concrete_TypeRef(), - 0, - requirement.as_mut_ptr(), - ))?; - - Ok(Self::wrap_under_create_rule(requirement.assume_init())) - } - } -} - -declare_TCFType! { - /// A code object representing signed code running on the system. - SecCode, SecCodeRef -} -impl_TCFType!(SecCode, SecCodeRef, SecCodeGetTypeID); - -impl Debug for SecCode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("SecCode") - } -} - -impl SecCode { - /// Retrieves the code object for the code making the call. - pub fn for_self(flags: Flags) -> Result<Self> { - let mut code = MaybeUninit::uninit(); - - unsafe { - cvt(SecCodeCopySelf(flags.bits(), code.as_mut_ptr()))?; - Ok(Self::wrap_under_create_rule(code.assume_init())) - } - } - - /// Performs dynamic validation of signed code. - pub fn check_validity(&self, flags: Flags, requirement: &SecRequirement) -> Result<()> { - unsafe { - cvt(SecCodeCheckValidity( - self.as_concrete_TypeRef(), - flags.bits(), - requirement.as_concrete_TypeRef(), - )) - } - } - - /// Asks a code host to identify one of its guests given - /// the type and value of specific attributes of the guest code. - /// - /// If `host` is `None` then the code signing root of trust (currently, the - // system kernel) should be used as the code host. - pub fn copy_guest_with_attribues( - host: Option<&SecCode>, - attrs: &GuestAttributes, - flags: Flags, - ) -> Result<SecCode> { - let mut code = MaybeUninit::uninit(); - - let host = match host { - Some(host) => host.as_concrete_TypeRef(), - None => std::ptr::null_mut(), - }; - - unsafe { - cvt(SecCodeCopyGuestWithAttributes( - host, - attrs.inner.as_concrete_TypeRef(), - flags.bits(), - code.as_mut_ptr(), - ))?; - - Ok(SecCode::wrap_under_create_rule(code.assume_init())) - } - } - - /// Retrieves the location on disk of signed code, given a code or static - /// code object. - pub fn path(&self, flags: Flags) -> Result<CFURL> { - let mut url = MaybeUninit::uninit(); - - // The docs say we can pass a SecCodeRef instead of a SecStaticCodeRef. - unsafe { - cvt(SecCodeCopyPath( - self.as_CFTypeRef() as _, - flags.bits(), - url.as_mut_ptr(), - ))?; - - Ok(CFURL::wrap_under_create_rule(url.assume_init())) - } - } -} - -declare_TCFType! { - /// A static code object representing signed code on disk. - SecStaticCode, SecStaticCodeRef -} -impl_TCFType!(SecStaticCode, SecStaticCodeRef, SecStaticCodeGetTypeID); - -impl SecStaticCode { - /// Creates a static code object representing the code at a specified file - /// system path. - pub fn from_path(path: &CFURL, flags: Flags) -> Result<Self> { - let mut code = MaybeUninit::uninit(); - - unsafe { - cvt(SecStaticCodeCreateWithPath( - path.as_concrete_TypeRef(), - flags.bits(), - code.as_mut_ptr(), - ))?; - - Ok(Self::wrap_under_create_rule(code.assume_init())) - } - } - - /// Retrieves the location on disk of signed code, given a code or static - /// code object. - pub fn path(&self, flags: Flags) -> Result<CFURL> { - let mut url = MaybeUninit::uninit(); - - // The docs say we can pass a SecCodeRef instead of a SecStaticCodeRef. - unsafe { - cvt(SecCodeCopyPath( - self.as_concrete_TypeRef(), - flags.bits(), - url.as_mut_ptr(), - ))?; - - Ok(CFURL::wrap_under_create_rule(url.assume_init())) - } - } - - /// Performs dynamic validation of signed code. - pub fn check_validity(&self, flags: Flags, requirement: &SecRequirement) -> Result<()> { - unsafe { - cvt(SecStaticCodeCheckValidity( - self.as_concrete_TypeRef(), - flags.bits(), - requirement.as_concrete_TypeRef(), - )) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use core_foundation::data::CFData; - use libc::{c_uint, c_void, KERN_SUCCESS}; - - #[test] - fn path_to_static_code_and_back() { - let path = CFURL::from_path("/bin/bash", false).unwrap(); - let code = SecStaticCode::from_path(&path, Flags::NONE).unwrap(); - assert_eq!(code.path(Flags::NONE).unwrap(), path); - } - - #[test] - fn self_to_path() { - let path = CFURL::from_path(std::env::current_exe().unwrap(), false).unwrap(); - let code = SecCode::for_self(Flags::NONE).unwrap(); - assert_eq!(code.path(Flags::NONE).unwrap(), path); - } - - #[test] - fn bash_is_signed_by_apple() { - let path = CFURL::from_path("/bin/bash", false).unwrap(); - let code = SecStaticCode::from_path(&path, Flags::NONE).unwrap(); - let requirement: SecRequirement = "anchor apple".parse().unwrap(); - code.check_validity(Flags::NONE, &requirement).unwrap(); - } - - #[cfg(target_arch = "aarch64")] - #[test] - fn self_is_not_signed_by_apple() { - let code = SecCode::for_self(Flags::NONE).unwrap(); - let requirement: SecRequirement = "anchor apple".parse().unwrap(); - - assert_eq!( - code.check_validity(Flags::NONE, &requirement) - .unwrap_err() - .code(), - // "code failed to satisfy specified code requirement(s)" - -67050 - ); - } - - #[cfg(not(target_arch = "aarch64"))] - #[test] - fn self_is_not_signed_by_apple() { - let code = SecCode::for_self(Flags::NONE).unwrap(); - let requirement: SecRequirement = "anchor apple".parse().unwrap(); - - assert_eq!( - code.check_validity(Flags::NONE, &requirement) - .unwrap_err() - .code(), - // "code object is not signed at all" - -67062 - ); - } - - #[test] - fn copy_kernel_guest_with_launchd_pid() { - let mut attrs = GuestAttributes::new(); - attrs.set_pid(1); - - assert_eq!( - SecCode::copy_guest_with_attribues(None, &attrs, Flags::NONE) - .unwrap() - .path(Flags::NONE) - .unwrap() - .get_string() - .to_string(), - "file:///sbin/launchd" - ); - } - - #[test] - fn copy_current_guest_with_launchd_pid() { - let host_code = SecCode::for_self(Flags::NONE).unwrap(); - - let mut attrs = GuestAttributes::new(); - attrs.set_pid(1); - - assert_eq!( - SecCode::copy_guest_with_attribues(Some(&host_code), &attrs, Flags::NONE) - .unwrap_err() - .code(), - // "host has no guest with the requested attributes" - -67065 - ); - } - - #[test] - fn copy_kernel_guest_with_unmatched_pid() { - let mut attrs = GuestAttributes::new(); - attrs.set_pid(999_999_999); - - assert_eq!( - SecCode::copy_guest_with_attribues(None, &attrs, Flags::NONE) - .unwrap_err() - .code(), - // "UNIX[No such process]" - 100003 - ); - } - - #[test] - fn copy_kernel_guest_with_current_token() { - let mut token: [u8; 32] = [0; 32]; - let mut token_len = 32u32; - - enum OpaqueTaskName {} - - extern "C" { - fn mach_task_self() -> *const OpaqueTaskName; - fn task_info( - task_name: *const OpaqueTaskName, - task_flavor: u32, - out: *mut c_void, - out_len: *mut u32, - ) -> i32; - } - - const TASK_AUDIT_TOKEN: c_uint = 15; - - let result = unsafe { - task_info( - mach_task_self(), - TASK_AUDIT_TOKEN, - token.as_mut_ptr().cast::<c_void>(), - &mut token_len, - ) - }; - - assert_eq!(result, KERN_SUCCESS); - - let token_data = CFData::from_buffer(&token); - - let mut attrs = GuestAttributes::new(); - attrs.set_audit_token(token_data.as_concrete_TypeRef()); - - assert_eq!( - SecCode::copy_guest_with_attribues(None, &attrs, Flags::NONE) - .unwrap() - .path(Flags::NONE) - .unwrap() - .to_path() - .unwrap(), - std::env::current_exe().unwrap() - ); - } - - #[test] - fn copy_kernel_guest_with_unmatched_token() { - let token: [u8; 32] = [0; 32]; - let token_data = CFData::from_buffer(&token); - - let mut attrs = GuestAttributes::new(); - attrs.set_audit_token(token_data.as_concrete_TypeRef()); - - assert_eq!( - SecCode::copy_guest_with_attribues(None, &attrs, Flags::NONE) - .unwrap_err() - .code(), - // "UNIX[No such process]" - 100003 - ); - } -} diff --git a/vendor/security-framework/src/os/macos/digest_transform.rs b/vendor/security-framework/src/os/macos/digest_transform.rs deleted file mode 100644 index c086ed1a..00000000 --- a/vendor/security-framework/src/os/macos/digest_transform.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! Digest Transform support - -use core_foundation::base::{CFIndex, TCFType}; -use core_foundation::data::CFData; -use core_foundation::error::CFError; -use core_foundation::string::CFString; -use core_foundation_sys::base::CFTypeRef; -use core_foundation_sys::data::CFDataRef; -use core_foundation_sys::string::CFStringRef; -use security_framework_sys::digest_transform::*; -use security_framework_sys::transform::kSecTransformInputAttributeName; -use std::ptr; - -use crate::os::macos::transform::SecTransform; - -#[derive(Debug, Copy, Clone)] -/// A type of digest. -pub struct DigestType(CFStringRef); - -#[allow(missing_docs)] -impl DigestType { - #[inline(always)] - #[must_use] - pub fn hmac_md5() -> Self { - unsafe { Self(kSecDigestHMACMD5) } - } - - #[inline(always)] - #[must_use] - pub fn hmac_sha1() -> Self { - unsafe { Self(kSecDigestHMACSHA1) } - } - - #[inline(always)] - #[must_use] - pub fn hmac_sha2() -> Self { - unsafe { Self(kSecDigestHMACSHA2) } - } - - #[inline(always)] - #[must_use] - pub fn md2() -> Self { - unsafe { Self(kSecDigestMD2) } - } - - #[inline(always)] - #[must_use] - pub fn md4() -> Self { - unsafe { Self(kSecDigestMD4) } - } - - #[inline(always)] - #[must_use] - pub fn md5() -> Self { - unsafe { Self(kSecDigestMD5) } - } - - #[inline(always)] - #[must_use] - pub fn sha1() -> Self { - unsafe { Self(kSecDigestSHA1) } - } - - #[inline(always)] - #[must_use] - pub fn sha2() -> Self { - unsafe { Self(kSecDigestSHA2) } - } - - #[inline(always)] - fn to_type(self) -> CFTypeRef { - self.0 as CFTypeRef - } -} - -/// A builder for digest transform operations. -pub struct Builder { - digest_type: Option<DigestType>, - digest_length: Option<CFIndex>, - hmac_key: Option<CFData>, -} - -impl Default for Builder { - #[inline(always)] - fn default() -> Self { - Self::new() - } -} - -impl Builder { - /// Returns a new builder with default settings. - #[inline(always)] - #[must_use] - pub fn new() -> Self { - Self { - digest_type: None, - digest_length: None, - hmac_key: None, - } - } - - /// Sets the type of digest to perform. - /// - /// If not set, an appropriate digest will be selected for you. - #[inline] - pub fn type_(&mut self, digest_type: DigestType) -> &mut Self { - self.digest_type = Some(digest_type); - self - } - - /// Sets the output length of the digest. - /// - /// If not set, an appropriate length will be selected for you. Some digest - /// types only support specific output lengths. - #[inline] - pub fn length(&mut self, digest_length: CFIndex) -> &mut Self { - self.digest_length = Some(digest_length); - self - } - - /// Sets the key used for HMAC digests. - /// - /// Only applies to `HmacMd5`, `HmacSha1`, and `HmacSha2` digests. - #[inline] - pub fn hmac_key(&mut self, hmac_key: CFData) -> &mut Self { - self.hmac_key = Some(hmac_key); - self - } - - /// Computes the digest of the data. - pub fn execute(&self, data: &CFData) -> Result<CFData, CFError> { - unsafe { - let digest_type = match self.digest_type { - Some(ref digest_type) => digest_type.to_type(), - None => ptr::null(), - }; - - let digest_length = self.digest_length.unwrap_or(0); - - let mut error = ptr::null_mut(); - let transform = SecDigestTransformCreate(digest_type, digest_length, &mut error); - if transform.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - let mut transform = SecTransform::wrap_under_create_rule(transform); - - if let Some(ref hmac_key) = self.hmac_key { - let key = CFString::wrap_under_get_rule(kSecDigestHMACKeyAttribute); - transform.set_attribute(&key, hmac_key)?; - } - - let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName); - transform.set_attribute(&key, data)?; - - let result = transform.execute()?; - Ok(CFData::wrap_under_get_rule( - result.as_CFTypeRef() as CFDataRef - )) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn md5() { - let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes()); - let hash = Builder::new() - .type_(DigestType::md5()) - .execute(&data) - .unwrap(); - assert_eq!( - hex::encode(hash.bytes()), - "9e107d9d372bb6826bd81d3542a419d6" - ); - } - - #[test] - fn hmac_sha1() { - let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes()); - let key = CFData::from_buffer(b"key"); - let hash = Builder::new() - .type_(DigestType::hmac_sha1()) - .hmac_key(key) - .execute(&data) - .unwrap(); - assert_eq!( - hex::encode(hash.bytes()), - "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9" - ); - } -} diff --git a/vendor/security-framework/src/os/macos/encrypt_transform.rs b/vendor/security-framework/src/os/macos/encrypt_transform.rs deleted file mode 100644 index 7b9736da..00000000 --- a/vendor/security-framework/src/os/macos/encrypt_transform.rs +++ /dev/null @@ -1,254 +0,0 @@ -//! Encryption and Decryption transform support. - -use core_foundation::base::TCFType; -use core_foundation::data::CFData; -use core_foundation::error::CFError; -use core_foundation::string::CFString; -use core_foundation_sys::data::CFDataRef; -use core_foundation_sys::string::CFStringRef; -use security_framework_sys::encrypt_transform::*; -use security_framework_sys::transform::kSecTransformInputAttributeName; -use std::ptr; - -use crate::key::SecKey; -use crate::os::macos::transform::SecTransform; - -#[derive(Debug, Copy, Clone)] -/// The padding scheme to use for encryption. -pub struct Padding(CFStringRef); - -impl Padding { - /// Do not pad. - #[inline(always)] - #[must_use] - pub fn none() -> Self { - unsafe { Self(kSecPaddingNoneKey) } - } - - /// Use PKCS#1 padding. - #[inline(always)] - #[must_use] - pub fn pkcs1() -> Self { - unsafe { Self(kSecPaddingPKCS1Key) } - } - - /// Use PKCS#5 padding. - #[inline(always)] - #[must_use] - pub fn pkcs5() -> Self { - unsafe { Self(kSecPaddingPKCS5Key) } - } - - /// Use PKCS#7 padding. - #[inline(always)] - #[must_use] - pub fn pkcs7() -> Self { - unsafe { Self(kSecPaddingPKCS7Key) } - } - - /// Use OAEP padding. - #[inline(always)] - #[must_use] - pub fn oaep() -> Self { - unsafe { Self(kSecPaddingOAEPKey) } - } - - #[inline] - fn to_str(self) -> CFString { - unsafe { CFString::wrap_under_get_rule(self.0) } - } -} - -/// The cipher mode to use. -/// -/// Only applies to AES encryption. -#[derive(Debug, Copy, Clone)] -pub struct Mode(CFStringRef); - -#[allow(missing_docs)] -impl Mode { - #[inline(always)] - #[must_use] - pub fn none() -> Self { - unsafe { Self(kSecModeNoneKey) } - } - - #[inline(always)] - #[must_use] - pub fn ecb() -> Self { - unsafe { Self(kSecModeECBKey) } - } - - #[inline(always)] - #[must_use] - pub fn cbc() -> Self { - unsafe { Self(kSecModeCBCKey) } - } - - #[inline(always)] - #[must_use] - pub fn cfb() -> Self { - unsafe { Self(kSecModeCFBKey) } - } - - #[inline(always)] - #[must_use] - pub fn ofb() -> Self { - unsafe { Self(kSecModeOFBKey) } - } - - fn to_str(self) -> CFString { - unsafe { CFString::wrap_under_get_rule(self.0) } - } -} - -/// A builder for encryption and decryption transform operations. -#[derive(Default)] -pub struct Builder { - padding: Option<Padding>, - mode: Option<Mode>, - iv: Option<CFData>, -} - -impl Builder { - /// Creates a new `Builder` with a default configuration. - #[inline(always)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Selects the padding scheme to use. - /// - /// If not set, an appropriate scheme will be selected for you. - #[inline(always)] - pub fn padding(&mut self, padding: Padding) -> &mut Self { - self.padding = Some(padding); - self - } - - /// Selects the encryption mode to use. - /// - /// If not set, an appropriate mode will be selected for you. - #[inline(always)] - pub fn mode(&mut self, mode: Mode) -> &mut Self { - self.mode = Some(mode); - self - } - - /// Sets the initialization vector to use. - /// - /// If not set, an appropriate value will be supplied for you. - #[inline(always)] - pub fn iv(&mut self, iv: CFData) -> &mut Self { - self.iv = Some(iv); - self - } - - /// Encrypts data with a provided key. - pub fn encrypt(&self, key: &SecKey, data: &CFData) -> Result<CFData, CFError> { - unsafe { - let mut error = ptr::null_mut(); - let transform = SecEncryptTransformCreate(key.as_concrete_TypeRef(), &mut error); - if transform.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - let transform = SecTransform::wrap_under_create_rule(transform); - - self.finish(transform, data) - } - } - - /// Decrypts data with a provided key. - pub fn decrypt(&self, key: &SecKey, data: &CFData) -> Result<CFData, CFError> { - unsafe { - let mut error = ptr::null_mut(); - let transform = SecDecryptTransformCreate(key.as_concrete_TypeRef(), &mut error); - if transform.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - let transform = SecTransform::wrap_under_create_rule(transform); - - self.finish(transform, data) - } - } - - fn finish(&self, mut transform: SecTransform, data: &CFData) -> Result<CFData, CFError> { - unsafe { - if let Some(ref padding) = self.padding { - let key = CFString::wrap_under_get_rule(kSecPaddingKey); - transform.set_attribute(&key, &padding.to_str())?; - } - - if let Some(ref mode) = self.mode { - let key = CFString::wrap_under_get_rule(kSecEncryptionMode); - transform.set_attribute(&key, &mode.to_str())?; - } - - if let Some(ref iv) = self.iv { - let key = CFString::wrap_under_get_rule(kSecIVKey); - transform.set_attribute(&key, iv)?; - } - - let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName); - transform.set_attribute(&key, data)?; - - let result = transform.execute()?; - Ok(CFData::wrap_under_get_rule( - result.as_CFTypeRef() as CFDataRef - )) - } - } -} - -#[cfg(test)] -mod test { - use hex::FromHex; - - use super::*; - use crate::os::macos::item::KeyType; - use crate::os::macos::key::SecKeyExt; - - #[test] - fn cbc_mmt_256() { - // test 9 - let key = "87725bd43a45608814180773f0e7ab95a3c859d83a2130e884190e44d14c6996"; - let iv = "e49651988ebbb72eb8bb80bb9abbca34"; - let ciphertext = "5b97a9d423f4b97413f388d9a341e727bb339f8e18a3fac2f2fb85abdc8f135deb30054a\ - 1afdc9b6ed7da16c55eba6b0d4d10c74e1d9a7cf8edfaeaa684ac0bd9f9d24ba674955c7\ - 9dc6be32aee1c260b558ff07e3a4d49d24162011ff254db8be078e8ad07e648e6bf56793\ - 76cb4321a5ef01afe6ad8816fcc7634669c8c4389295c9241e45fff39f3225f7745032da\ - eebe99d4b19bcb215d1bfdb36eda2c24"; - let plaintext = "bfe5c6354b7a3ff3e192e05775b9b75807de12e38a626b8bf0e12d5fff78e4f1775aa7d79\ - 2d885162e66d88930f9c3b2cdf8654f56972504803190386270f0aa43645db187af41fcea\ - 639b1f8026ccdd0c23e0de37094a8b941ecb7602998a4b2604e69fc04219585d854600e0a\ - d6f99a53b2504043c08b1c3e214d17cde053cbdf91daa999ed5b47c37983ba3ee254bc5c7\ - 93837daaa8c85cfc12f7f54f699f"; - - let key = Vec::<u8>::from_hex(key).unwrap(); - let key = CFData::from_buffer(&key); - let key = SecKey::from_data(KeyType::aes(), &key).unwrap(); - - let iv = Vec::<u8>::from_hex(iv).unwrap(); - - let ciphertext = Vec::<u8>::from_hex(ciphertext).unwrap(); - - let plaintext = Vec::<u8>::from_hex(plaintext).unwrap(); - - let decrypted = Builder::new() - .padding(Padding::none()) - .iv(CFData::from_buffer(&iv)) - .decrypt(&key, &CFData::from_buffer(&ciphertext)) - .unwrap(); - - assert_eq!(plaintext, decrypted.bytes()); - - let encrypted = Builder::new() - .padding(Padding::none()) - .iv(CFData::from_buffer(&iv)) - .encrypt(&key, &CFData::from_buffer(&plaintext)) - .unwrap(); - - assert_eq!(ciphertext, encrypted.bytes()); - } -} diff --git a/vendor/security-framework/src/os/macos/identity.rs b/vendor/security-framework/src/os/macos/identity.rs deleted file mode 100644 index 0f8da464..00000000 --- a/vendor/security-framework/src/os/macos/identity.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! OSX specific extensions to identity functionality. -use core_foundation::array::CFArray; -use core_foundation::base::TCFType; -use security_framework_sys::identity::SecIdentityCreateWithCertificate; -use std::ptr; - -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::cvt; -use crate::identity::SecIdentity; -use crate::os::macos::keychain::SecKeychain; - -/// An extension trait adding OSX specific functionality to `SecIdentity`. -pub trait SecIdentityExt { - /// Creates an identity corresponding to a certificate, looking in the - /// provided keychains for the corresponding private key. - /// - /// To search the default keychains, use an empty slice for `keychains`. - /// - /// <https://developer.apple.com/documentation/security/1401160-secidentitycreatewithcertificate> - fn with_certificate( - keychains: &[SecKeychain], - certificate: &SecCertificate, - ) -> Result<SecIdentity>; -} - -impl SecIdentityExt for SecIdentity { - fn with_certificate(keychains: &[SecKeychain], certificate: &SecCertificate) -> Result<Self> { - let keychains = CFArray::from_CFTypes(keychains); - unsafe { - let mut identity = ptr::null_mut(); - cvt(SecIdentityCreateWithCertificate( - if keychains.len() > 0 {keychains.as_CFTypeRef()} else {ptr::null()}, - certificate.as_concrete_TypeRef(), - &mut identity, - ))?; - Ok(Self::wrap_under_create_rule(identity)) - } - } -} - -#[cfg(test)] -mod test { - use tempfile::tempdir; - - use super::*; - use crate::os::macos::certificate::SecCertificateExt; - use crate::os::macos::import_export::ImportOptions; - use crate::os::macos::keychain::CreateOptions; - use crate::os::macos::test::identity; - use crate::test; - - #[test] - fn certificate() { - let dir = p!(tempdir()); - let identity = identity(dir.path()); - let certificate = p!(identity.certificate()); - assert_eq!("foobar.com", p!(certificate.common_name())); - } - - #[test] - fn private_key() { - let dir = p!(tempdir()); - let identity = identity(dir.path()); - p!(identity.private_key()); - } - - #[test] - fn with_certificate() { - let dir = p!(tempdir()); - - let mut keychain = p!(CreateOptions::new() - .password("foobar") - .create(dir.path().join("test.keychain"))); - - let key = include_bytes!("../../../test/server.key"); - p!(ImportOptions::new() - .filename("server.key") - .keychain(&mut keychain) - .import(key)); - - let cert = test::certificate(); - p!(SecIdentity::with_certificate(&[keychain], &cert)); - } -} diff --git a/vendor/security-framework/src/os/macos/import_export.rs b/vendor/security-framework/src/os/macos/import_export.rs deleted file mode 100644 index 830d9483..00000000 --- a/vendor/security-framework/src/os/macos/import_export.rs +++ /dev/null @@ -1,344 +0,0 @@ -//! OSX specific extensions to import/export functionality. - -use core_foundation::array::CFArray; -use core_foundation::base::{CFType, TCFType}; -use core_foundation::data::CFData; -use core_foundation::string::CFString; -use security_framework_sys::base::errSecSuccess; -use security_framework_sys::import_export::*; -use std::ptr; -use std::str::FromStr; - -use crate::base::{Error, Result}; -use crate::certificate::SecCertificate; -use crate::identity::SecIdentity; -use crate::import_export::Pkcs12ImportOptions; -use crate::key::SecKey; -use crate::os::macos::access::SecAccess; -use crate::os::macos::keychain::SecKeychain; - -/// An extension trait adding OSX specific functionality to `Pkcs12ImportOptions`. -pub trait Pkcs12ImportOptionsExt { - /// Specifies the keychain in which to import the identity. - /// - /// If this is not called, the default keychain will be used. - fn keychain(&mut self, keychain: SecKeychain) -> &mut Self; - - /// Specifies the access control to be associated with the identity. - fn access(&mut self, access: SecAccess) -> &mut Self; -} - -impl Pkcs12ImportOptionsExt for Pkcs12ImportOptions { - #[inline(always)] - fn keychain(&mut self, keychain: SecKeychain) -> &mut Self { - crate::Pkcs12ImportOptionsInternals::keychain(self, keychain) - } - - #[inline(always)] - fn access(&mut self, access: SecAccess) -> &mut Self { - crate::Pkcs12ImportOptionsInternals::access(self, access) - } -} - -/// A builder type to import Security Framework types from serialized formats. -#[derive(Default)] -pub struct ImportOptions<'a> { - filename: Option<CFString>, - passphrase: Option<CFType>, - secure_passphrase: bool, - no_access_control: bool, - alert_title: Option<CFString>, - alert_prompt: Option<CFString>, - items: Option<&'a mut SecItems>, - keychain: Option<SecKeychain>, -} - -impl<'a> ImportOptions<'a> { - /// Creates a new builder with default options. - #[inline(always)] - #[must_use] - pub fn new() -> ImportOptions<'a> { - ImportOptions::default() - } - - /// Sets the filename from which the imported data came. - /// - /// The extension of the file will used as a hint for parsing. - #[inline] - pub fn filename(&mut self, filename: &str) -> &mut ImportOptions<'a> { - self.filename = Some(CFString::from_str(filename).unwrap()); - self - } - - /// Sets the passphrase to be used to decrypt the imported data. - #[inline] - pub fn passphrase(&mut self, passphrase: &str) -> &mut ImportOptions<'a> { - self.passphrase = Some(CFString::from_str(passphrase).unwrap().into_CFType()); - self - } - - /// Sets the passphrase to be used to decrypt the imported data. - #[inline] - pub fn passphrase_bytes(&mut self, passphrase: &[u8]) -> &mut ImportOptions<'a> { - self.passphrase = Some(CFData::from_buffer(passphrase).into_CFType()); - self - } - - /// If set, the user will be prompted to imput the passphrase used to - /// decrypt the imported data. - #[inline(always)] - pub fn secure_passphrase(&mut self, secure_passphrase: bool) -> &mut ImportOptions<'a> { - self.secure_passphrase = secure_passphrase; - self - } - - /// If set, imported items will have no access controls imposed on them. - #[inline(always)] - pub fn no_access_control(&mut self, no_access_control: bool) -> &mut ImportOptions<'a> { - self.no_access_control = no_access_control; - self - } - - /// Sets the title of the alert popup used with the `secure_passphrase` - /// option. - #[inline] - pub fn alert_title(&mut self, alert_title: &str) -> &mut ImportOptions<'a> { - self.alert_title = Some(CFString::from_str(alert_title).unwrap()); - self - } - - /// Sets the prompt of the alert popup used with the `secure_passphrase` - /// option. - #[inline] - pub fn alert_prompt(&mut self, alert_prompt: &str) -> &mut ImportOptions<'a> { - self.alert_prompt = Some(CFString::from_str(alert_prompt).unwrap()); - self - } - - /// Sets the object into which imported items will be placed. - #[inline(always)] - pub fn items(&mut self, items: &'a mut SecItems) -> &mut ImportOptions<'a> { - self.items = Some(items); - self - } - - /// Sets the keychain into which items will be imported. - /// - /// This must be specified to import `SecIdentity`s. - #[inline] - pub fn keychain(&mut self, keychain: &SecKeychain) -> &mut ImportOptions<'a> { - self.keychain = Some(keychain.clone()); - self - } - - /// Imports items from serialized data. - pub fn import(&mut self, data: &[u8]) -> Result<()> { - let data = CFData::from_buffer(data); - let data = data.as_concrete_TypeRef(); - - let filename = match self.filename { - Some(ref filename) => filename.as_concrete_TypeRef(), - None => ptr::null(), - }; - - let mut key_params = SecItemImportExportKeyParameters { - version: SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION, - flags: 0, - passphrase: ptr::null(), - alertTitle: ptr::null(), - alertPrompt: ptr::null(), - accessRef: ptr::null_mut(), - keyUsage: ptr::null_mut(), - keyAttributes: ptr::null(), - }; - - if let Some(ref passphrase) = self.passphrase { - key_params.passphrase = passphrase.as_CFTypeRef(); - } - - if self.secure_passphrase { - key_params.flags |= kSecKeySecurePassphrase; - } - - if self.no_access_control { - key_params.flags |= kSecKeyNoAccessControl; - } - - if let Some(ref alert_title) = self.alert_title { - key_params.alertTitle = alert_title.as_concrete_TypeRef(); - } - - if let Some(ref alert_prompt) = self.alert_prompt { - key_params.alertPrompt = alert_prompt.as_concrete_TypeRef(); - } - - let keychain = match self.keychain { - Some(ref keychain) => keychain.as_concrete_TypeRef(), - None => ptr::null_mut(), - }; - - let mut raw_items = ptr::null(); - let items_ref = match self.items { - Some(_) => std::ptr::addr_of_mut!(raw_items), - None => ptr::null_mut(), - }; - - unsafe { - let ret = SecItemImport( - data, - filename, - ptr::null_mut(), - ptr::null_mut(), - 0, - &key_params, - keychain, - items_ref, - ); - if ret != errSecSuccess { - return Err(Error::from_code(ret)); - } - - if let Some(ref mut items) = self.items { - let raw_items = CFArray::<CFType>::wrap_under_create_rule(raw_items); - for item in raw_items.iter() { - let type_id = item.type_of(); - if type_id == SecCertificate::type_id() { - items.certificates.push(SecCertificate::wrap_under_get_rule( - item.as_CFTypeRef() as *mut _, - )); - } else if type_id == SecIdentity::type_id() { - items.identities.push(SecIdentity::wrap_under_get_rule( - item.as_CFTypeRef() as *mut _, - )); - } else if type_id == SecKey::type_id() { - items - .keys - .push(SecKey::wrap_under_get_rule(item.as_CFTypeRef() as *mut _)); - } else { - panic!("Got bad type from SecItemImport: {type_id}"); - } - } - } - } - - Ok(()) - } -} - -/// A type which holds items imported from serialized data. -/// -/// Pass a reference to `ImportOptions::items`. -#[derive(Default)] -pub struct SecItems { - /// Imported certificates. - pub certificates: Vec<SecCertificate>, - /// Imported identities. - pub identities: Vec<SecIdentity>, - /// Imported keys. - pub keys: Vec<SecKey>, -} - -#[cfg(test)] -mod test { - use super::*; - use crate::import_export::*; - use crate::os::macos::keychain; - use tempfile::tempdir; - - #[test] - fn certificate() { - let data = include_bytes!("../../../test/server.der"); - let mut items = SecItems::default(); - ImportOptions::new() - .filename("server.der") - .items(&mut items) - .import(data) - .unwrap(); - assert_eq!(1, items.certificates.len()); - assert_eq!(0, items.identities.len()); - assert_eq!(0, items.keys.len()); - } - - #[test] - fn key() { - let data = include_bytes!("../../../test/server.key"); - let mut items = SecItems::default(); - ImportOptions::new() - .filename("server.key") - .items(&mut items) - .import(data) - .unwrap(); - assert_eq!(0, items.certificates.len()); - assert_eq!(0, items.identities.len()); - assert_eq!(1, items.keys.len()); - } - - #[test] - fn identity() { - let dir = tempdir().unwrap(); - let keychain = keychain::CreateOptions::new() - .password("password") - .create(dir.path().join("identity.keychain")) - .unwrap(); - - let data = include_bytes!("../../../test/server.p12"); - let mut items = SecItems::default(); - ImportOptions::new() - .filename("server.p12") - .passphrase("password123") - .items(&mut items) - .keychain(&keychain) - .import(data) - .unwrap(); - assert_eq!(1, items.identities.len()); - assert_eq!(0, items.certificates.len()); - assert_eq!(0, items.keys.len()); - } - - #[test] - #[ignore] // since it requires manual intervention - fn secure_passphrase_identity() { - let dir = tempdir().unwrap(); - let keychain = keychain::CreateOptions::new() - .password("password") - .create(dir.path().join("identity.keychain")) - .unwrap(); - - let data = include_bytes!("../../../test/server.p12"); - let mut items = SecItems::default(); - ImportOptions::new() - .filename("server.p12") - .secure_passphrase(true) - .alert_title("alert title") - .alert_prompt("alert prompt") - .items(&mut items) - .keychain(&keychain) - .import(data) - .unwrap(); - assert_eq!(1, items.identities.len()); - assert_eq!(0, items.certificates.len()); - assert_eq!(0, items.keys.len()); - } - - #[test] - fn pkcs12_import() { - use super::Pkcs12ImportOptionsExt; - - let dir = tempdir().unwrap(); - let keychain = keychain::CreateOptions::new() - .password("password") - .create(dir.path().join("pkcs12_import")) - .unwrap(); - - let data = include_bytes!("../../../test/server.p12"); - let identities = p!(Pkcs12ImportOptions::new() - .passphrase("password123") - .keychain(keychain) - .import(data)); - assert_eq!(1, identities.len()); - assert_eq!( - hex::encode(identities[0].key_id.as_ref().unwrap()), - "ed6492936dcc8907e397e573b36e633458dc33f1" - ); - } -} diff --git a/vendor/security-framework/src/os/macos/item.rs b/vendor/security-framework/src/os/macos/item.rs deleted file mode 100644 index 18a4d2e8..00000000 --- a/vendor/security-framework/src/os/macos/item.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! OSX specific functionality for items. -use crate::item::ItemSearchOptions; -use crate::os::macos::keychain::SecKeychain; -use crate::ItemSearchOptionsInternals; - -// Moved to crate::Key -pub use crate::key::KeyType; - -/// An extension trait adding OSX specific functionality to `ItemSearchOptions`. -pub trait ItemSearchOptionsExt { - /// Search within the specified keychains. - /// - /// If this is not called, the default keychain will be searched. - fn keychains(&mut self, keychains: &[SecKeychain]) -> &mut Self; -} - -impl ItemSearchOptionsExt for ItemSearchOptions { - #[inline(always)] - fn keychains(&mut self, keychains: &[SecKeychain]) -> &mut Self { - ItemSearchOptionsInternals::keychains(self, keychains) - } -} - -#[cfg(test)] -mod test { - use crate::item::*; - use crate::os::macos::certificate::SecCertificateExt; - use crate::os::macos::item::ItemSearchOptionsExt; - use crate::os::macos::test::keychain; - use tempfile::tempdir; - - #[test] - fn find_certificate() { - let dir = p!(tempdir()); - let keychain = keychain(dir.path()); - let results = p!(ItemSearchOptions::new() - .keychains(&[keychain]) - .class(ItemClass::certificate()) - .search()); - assert_eq!(1, results.len()); - let certificate = match results[0] { - SearchResult::Ref(Reference::Certificate(ref cert)) => cert, - _ => panic!("expected certificate"), - }; - assert_eq!("foobar.com", p!(certificate.common_name())); - } -} diff --git a/vendor/security-framework/src/os/macos/key.rs b/vendor/security-framework/src/os/macos/key.rs deleted file mode 100644 index f6a20e93..00000000 --- a/vendor/security-framework/src/os/macos/key.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! OSX specific functionality for keys. -use core_foundation::base::TCFType; -use core_foundation::data::CFData; -use core_foundation::dictionary::CFDictionary; -use core_foundation::error::CFError; -use core_foundation::string::CFString; -use security_framework_sys::item::kSecAttrKeyType; -use security_framework_sys::key::SecKeyCreateFromData; -use std::ptr; - -use crate::key::{KeyType, SecKey}; - -/// An extension trait adding OSX specific functionality to `SecKey`. -pub trait SecKeyExt { - /// Creates a new `SecKey` from a buffer containing key data. - fn from_data(key_type: KeyType, key_data: &CFData) -> Result<SecKey, CFError>; -} - -impl SecKeyExt for SecKey { - fn from_data(key_type: KeyType, key_data: &CFData) -> Result<Self, CFError> { - unsafe { - let key = CFString::wrap_under_get_rule(kSecAttrKeyType); - let dict = CFDictionary::from_CFType_pairs(&[(key, key_type.to_str())]); - - let mut err = ptr::null_mut(); - let key = SecKeyCreateFromData( - dict.as_concrete_TypeRef(), - key_data.as_concrete_TypeRef(), - &mut err, - ); - if key.is_null() { - Err(CFError::wrap_under_create_rule(err)) - } else { - Ok(Self::wrap_under_create_rule(key)) - } - } - } -} diff --git a/vendor/security-framework/src/os/macos/keychain.rs b/vendor/security-framework/src/os/macos/keychain.rs deleted file mode 100644 index bce5e00a..00000000 --- a/vendor/security-framework/src/os/macos/keychain.rs +++ /dev/null @@ -1,281 +0,0 @@ -//! Keychain support. - -use core_foundation::base::{Boolean, TCFType}; -use security_framework_sys::base::{errSecSuccess, SecKeychainRef}; -use security_framework_sys::keychain::*; -use std::ffi::CString; -use std::os::raw::c_void; -use std::os::unix::ffi::OsStrExt; -use std::path::Path; -use std::ptr; - -use crate::base::{Error, Result}; -use crate::cvt; -use crate::os::macos::access::SecAccess; - -pub use security_framework_sys::keychain::SecPreferencesDomain; - -declare_TCFType! { - /// A type representing a keychain. - SecKeychain, SecKeychainRef -} -impl_TCFType!(SecKeychain, SecKeychainRef, SecKeychainGetTypeID); - -unsafe impl Sync for SecKeychain {} -unsafe impl Send for SecKeychain {} - -impl SecKeychain { - /// Creates a `SecKeychain` object corresponding to the user's default - /// keychain. - #[inline] - #[allow(clippy::should_implement_trait)] - pub fn default() -> Result<Self> { - unsafe { - let mut keychain = ptr::null_mut(); - cvt(SecKeychainCopyDefault(&mut keychain))?; - Ok(Self::wrap_under_create_rule(keychain)) - } - } - - /// Creates a `SecKeychain` object corresponding to the user's default - /// keychain for the given domain. - pub fn default_for_domain(domain: SecPreferencesDomain) -> Result<Self> { - unsafe { - let mut keychain = ptr::null_mut(); - cvt(SecKeychainCopyDomainDefault(domain, &mut keychain))?; - Ok(Self::wrap_under_create_rule(keychain)) - } - } - - /// Opens a keychain from a file. - pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> { - let path_name = [ - path.as_ref().as_os_str().as_bytes(), - std::slice::from_ref(&0) - ].concat(); - - unsafe { - let mut keychain = ptr::null_mut(); - cvt(SecKeychainOpen(path_name.as_ptr().cast(), &mut keychain))?; - Ok(Self::wrap_under_create_rule(keychain)) - } - } - - /// Unlocks the keychain. - /// - /// If a password is not specified, the user will be prompted to enter it. - pub fn unlock(&mut self, password: Option<&str>) -> Result<()> { - let (len, ptr, use_password) = match password { - Some(password) => (password.len(), password.as_ptr().cast(), true), - None => (0, ptr::null(), false), - }; - - unsafe { - cvt(SecKeychainUnlock( - self.as_concrete_TypeRef(), - len as u32, - ptr, - Boolean::from(use_password), - )) - } - } - - /// Sets settings of the keychain. - #[inline] - pub fn set_settings(&mut self, settings: &KeychainSettings) -> Result<()> { - unsafe { - cvt(SecKeychainSetSettings( - self.as_concrete_TypeRef(), - &settings.0, - )) - } - } - - #[cfg(target_os = "macos")] - /// Disables the user interface for keychain services functions that - /// automatically display a user interface. - pub fn disable_user_interaction() -> Result<KeychainUserInteractionLock> { - let code = unsafe { SecKeychainSetUserInteractionAllowed(0u8) }; - - if code != errSecSuccess { - Err(Error::from_code(code)) - } else { - Ok(KeychainUserInteractionLock) - } - } - - #[cfg(target_os = "macos")] - /// Indicates whether keychain services functions that normally display a - /// user interaction are allowed to do so. - pub fn user_interaction_allowed() -> Result<bool> { - let mut state: Boolean = 0; - let code = unsafe { SecKeychainGetUserInteractionAllowed(&mut state) }; - - if code != errSecSuccess { - Err(Error::from_code(code)) - } else { - Ok(state != 0) - } - } -} - -/// A builder type to create new keychains. -#[derive(Default)] -pub struct CreateOptions { - password: Option<String>, - prompt_user: bool, - access: Option<SecAccess>, -} - -impl CreateOptions { - /// Creates a new builder with default options. - #[inline(always)] - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Sets the password to be used to protect the keychain. - #[inline] - pub fn password(&mut self, password: &str) -> &mut Self { - self.password = Some(password.into()); - self - } - - /// If set, the user will be prompted to provide a password used to - /// protect the keychain. - #[inline(always)] - pub fn prompt_user(&mut self, prompt_user: bool) -> &mut Self { - self.prompt_user = prompt_user; - self - } - - /// Sets the access control applied to the keychain. - #[inline(always)] - pub fn access(&mut self, access: SecAccess) -> &mut Self { - self.access = Some(access); - self - } - - /// Creates a new keychain at the specified location on the filesystem. - pub fn create<P: AsRef<Path>>(&self, path: P) -> Result<SecKeychain> { - unsafe { - let path_name = path.as_ref().as_os_str().as_bytes(); - // FIXME - let path_name = CString::new(path_name).unwrap(); - - let (password, password_len) = match self.password { - Some(ref password) => (password.as_ptr().cast::<c_void>(), password.len() as u32), - None => (ptr::null(), 0), - }; - - let access = match self.access { - Some(ref access) => access.as_concrete_TypeRef(), - None => ptr::null_mut(), - }; - - let mut keychain = ptr::null_mut(); - cvt(SecKeychainCreate( - path_name.as_ptr(), - password_len, - password, - Boolean::from(self.prompt_user), - access, - &mut keychain, - ))?; - - Ok(SecKeychain::wrap_under_create_rule(keychain)) - } - } -} - -/// Settings associated with a `SecKeychain`. -pub struct KeychainSettings(SecKeychainSettings); - -impl KeychainSettings { - /// Creates a new `KeychainSettings` with default settings. - #[inline] - #[must_use] - pub fn new() -> Self { - Self(SecKeychainSettings { - version: SEC_KEYCHAIN_SETTINGS_VERS1, - lockOnSleep: 0, - useLockInterval: 0, - lockInterval: i32::max_value() as u32, - }) - } - - /// If set, the keychain will automatically lock when the computer sleeps. - /// - /// Defaults to `false`. - #[inline(always)] - pub fn set_lock_on_sleep(&mut self, lock_on_sleep: bool) { - self.0.lockOnSleep = Boolean::from(lock_on_sleep); - } - - /// Sets the interval of time in seconds after which the keychain is - /// automatically locked. - /// - /// Defaults to `None`. - pub fn set_lock_interval(&mut self, lock_interval: Option<u32>) { - match lock_interval { - Some(lock_interval) => { - self.0.useLockInterval = 1; - self.0.lockInterval = lock_interval; - } - None => { - self.0.useLockInterval = 0; - self.0.lockInterval = i32::max_value() as u32; - } - } - } -} - -impl Default for KeychainSettings { - #[inline(always)] - fn default() -> Self { - Self::new() - } -} - -#[cfg(target_os = "macos")] -#[must_use = "The user interaction is disabled for the lifetime of the returned object"] -/// Automatically re-enables user interaction. -pub struct KeychainUserInteractionLock; - -#[cfg(target_os = "macos")] -impl Drop for KeychainUserInteractionLock { - #[inline(always)] - fn drop(&mut self) { - unsafe { SecKeychainSetUserInteractionAllowed(1u8) }; - } -} - -#[cfg(test)] -mod test { - use tempfile::tempdir; - - use super::*; - - #[test] - fn create_options() { - let dir = tempdir().unwrap(); - - let mut keychain = CreateOptions::new() - .password("foobar") - .create(dir.path().join("test.keychain")) - .unwrap(); - - keychain.set_settings(&KeychainSettings::new()).unwrap(); - } - - #[test] - fn disable_user_interaction() { - assert!(SecKeychain::user_interaction_allowed().unwrap()); - { - let _lock = SecKeychain::disable_user_interaction().unwrap(); - assert!(!SecKeychain::user_interaction_allowed().unwrap()); - } - assert!(SecKeychain::user_interaction_allowed().unwrap()); - } -} diff --git a/vendor/security-framework/src/os/macos/keychain_item.rs b/vendor/security-framework/src/os/macos/keychain_item.rs deleted file mode 100644 index fd7b452a..00000000 --- a/vendor/security-framework/src/os/macos/keychain_item.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Keychain item support. - -use core_foundation::base::TCFType; -use security_framework_sys::base::SecKeychainItemRef; -use security_framework_sys::keychain_item::SecKeychainItemGetTypeID; -use std::fmt; - -declare_TCFType! { - /// A type representing a keychain item. - SecKeychainItem, SecKeychainItemRef -} -impl_TCFType!( - SecKeychainItem, - SecKeychainItemRef, - SecKeychainItemGetTypeID -); - -unsafe impl Sync for SecKeychainItem {} -unsafe impl Send for SecKeychainItem {} - -impl fmt::Debug for SecKeychainItem { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("SecKeychainItem").finish_non_exhaustive() - } -} diff --git a/vendor/security-framework/src/os/macos/mod.rs b/vendor/security-framework/src/os/macos/mod.rs deleted file mode 100644 index 3e468fc3..00000000 --- a/vendor/security-framework/src/os/macos/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! OSX specific extensions. - -pub mod access; -pub mod certificate; -pub mod certificate_oids; -pub mod code_signing; -pub mod digest_transform; -pub mod encrypt_transform; -pub mod identity; -pub mod import_export; -pub mod item; -pub mod key; -pub mod keychain; -pub mod keychain_item; -pub mod passwords; -pub mod secure_transport; -pub mod transform; - -#[cfg(test)] -pub mod test { - use crate::identity::SecIdentity; - use crate::item::{ItemClass, ItemSearchOptions, Reference, SearchResult}; - use crate::os::macos::item::ItemSearchOptionsExt; - use crate::os::macos::keychain::SecKeychain; - use std::fs::File; - use std::io::prelude::*; - use std::path::Path; - - #[must_use] pub fn identity(dir: &Path) -> SecIdentity { - // FIXME https://github.com/rust-lang/rust/issues/30018 - let keychain = keychain(dir); - let mut items = p!(ItemSearchOptions::new() - .class(ItemClass::identity()) - .keychains(&[keychain]) - .search()); - match items.pop().unwrap() { - SearchResult::Ref(Reference::Identity(identity)) => identity, - _ => panic!("expected identity"), - } - } - - #[must_use] pub fn keychain(dir: &Path) -> SecKeychain { - let path = dir.join("server.keychain"); - let mut file = p!(File::create(&path)); - p!(file.write_all(include_bytes!("../../../test/server.keychain"))); - drop(file); - - let mut keychain = p!(SecKeychain::open(&path)); - p!(keychain.unlock(Some("password123"))); - keychain - } -} diff --git a/vendor/security-framework/src/os/macos/passwords.rs b/vendor/security-framework/src/os/macos/passwords.rs deleted file mode 100644 index 8587a3c5..00000000 --- a/vendor/security-framework/src/os/macos/passwords.rs +++ /dev/null @@ -1,525 +0,0 @@ -//! Password support. - -use crate::os::macos::keychain::SecKeychain; -use crate::os::macos::keychain_item::SecKeychainItem; -use core_foundation::array::CFArray; -use core_foundation::base::TCFType; -pub use security_framework_sys::keychain::{SecAuthenticationType, SecProtocolType}; -use security_framework_sys::keychain::{ - SecKeychainAddGenericPassword, SecKeychainAddInternetPassword, SecKeychainFindGenericPassword, - SecKeychainFindInternetPassword, -}; -use security_framework_sys::keychain_item::{ - SecKeychainItemDelete, SecKeychainItemFreeContent, SecKeychainItemModifyAttributesAndData, -}; -use std::fmt; -use std::fmt::Write; -use std::ops::Deref; -use std::ptr; -use std::slice; - -use crate::base::Result; -use crate::cvt; - -/// Password slice. Use `.as_ref()` to get `&[u8]` or `.to_owned()` to get `Vec<u8>` -pub struct SecKeychainItemPassword { - data: *const u8, - data_len: usize, -} - -impl fmt::Debug for SecKeychainItemPassword { - #[cold] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for _ in 0..self.data_len { - f.write_char('•')?; - } - Ok(()) - } -} - -impl AsRef<[u8]> for SecKeychainItemPassword { - #[inline] - fn as_ref(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.data, self.data_len) } - } -} - -impl Deref for SecKeychainItemPassword { - type Target = [u8]; - #[inline(always)] - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - -impl Drop for SecKeychainItemPassword { - #[inline] - fn drop(&mut self) { - unsafe { - SecKeychainItemFreeContent(ptr::null_mut(), self.data as *mut _); - } - } -} - -impl SecKeychainItem { - /// Modify keychain item in-place, replacing its password with the given one - pub fn set_password(&mut self, password: &[u8]) -> Result<()> { - unsafe { - cvt(SecKeychainItemModifyAttributesAndData( - self.as_CFTypeRef() as *mut _, - ptr::null(), - password.len() as u32, - password.as_ptr().cast(), - ))?; - } - Ok(()) - } - - /// Delete this item from its keychain - #[inline] - pub fn delete(self) { - unsafe { - SecKeychainItemDelete(self.as_CFTypeRef() as *mut _); - } - } -} - -/// Find a generic password. -/// -/// The underlying system supports passwords with 0 values, so this -/// returns a vector of bytes rather than a string. -/// -/// * `keychains` is an array of keychains to search or None to search -/// the default keychain. -/// * `service` is the name of the service to search for. -/// * `account` is the name of the account to search for. -pub fn find_generic_password( - keychains: Option<&[SecKeychain]>, - service: &str, - account: &str, -) -> Result<(SecKeychainItemPassword, SecKeychainItem)> { - let keychains_or_none = keychains.map(CFArray::from_CFTypes); - - let keychains_or_null = match keychains_or_none { - None => ptr::null(), - Some(ref keychains) => keychains.as_CFTypeRef(), - }; - - let mut data_len = 0; - let mut data = ptr::null_mut(); - let mut item = ptr::null_mut(); - - unsafe { - cvt(SecKeychainFindGenericPassword( - keychains_or_null, - service.len() as u32, - service.as_ptr().cast(), - account.len() as u32, - account.as_ptr().cast(), - &mut data_len, - &mut data, - &mut item, - ))?; - Ok(( - SecKeychainItemPassword { - data: data as *const _, - data_len: data_len as usize, - }, - SecKeychainItem::wrap_under_create_rule(item), - )) - } -} - -/// * `keychains` is an array of keychains to search or None to search -/// the default keychain. -/// * `server`: server name. -/// * `security_domain`: security domain. This parameter is optional. -/// * `account`: account name. -/// * `path`: the path. -/// * `port`: The TCP/IP port number. -/// * `protocol`: The protocol associated with this password. -/// * `authentication_type`: The authentication scheme used. -#[allow(clippy::too_many_arguments)] -pub fn find_internet_password( - keychains: Option<&[SecKeychain]>, - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, -) -> Result<(SecKeychainItemPassword, SecKeychainItem)> { - let keychains_or_none = keychains.map(CFArray::from_CFTypes); - - let keychains_or_null = match keychains_or_none { - None => ptr::null(), - Some(ref keychains) => keychains.as_CFTypeRef(), - }; - - let mut data_len = 0; - let mut data = ptr::null_mut(); - let mut item = ptr::null_mut(); - - unsafe { - cvt(SecKeychainFindInternetPassword( - keychains_or_null, - server.len() as u32, - server.as_ptr().cast(), - security_domain.map_or(0, |s| s.len() as u32), - security_domain - .map_or(ptr::null(), |s| s.as_ptr().cast()), - account.len() as u32, - account.as_ptr().cast(), - path.len() as u32, - path.as_ptr().cast(), - port.unwrap_or(0), - protocol, - authentication_type, - &mut data_len, - &mut data, - &mut item, - ))?; - Ok(( - SecKeychainItemPassword { - data: data as *const _, - data_len: data_len as usize, - }, - SecKeychainItem::wrap_under_create_rule(item), - )) - } -} - -impl SecKeychain { - /// Find application password in this keychain - #[inline] - pub fn find_generic_password( - &self, - service: &str, - account: &str, - ) -> Result<(SecKeychainItemPassword, SecKeychainItem)> { - find_generic_password(Some(&[self.clone()]), service, account) - } - - /// Find internet password in this keychain - #[inline] - #[allow(clippy::too_many_arguments)] - pub fn find_internet_password( - &self, - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, - ) -> Result<(SecKeychainItemPassword, SecKeychainItem)> { - find_internet_password( - Some(&[self.clone()]), - server, - security_domain, - account, - path, - port, - protocol, - authentication_type, - ) - } - - /// Update existing or add new internet password - #[allow(clippy::too_many_arguments)] - pub fn set_internet_password( - &self, - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, - password: &[u8], - ) -> Result<()> { - match self.find_internet_password( - server, - security_domain, - account, - path, - port, - protocol, - authentication_type, - ) { - Ok((_, mut item)) => item.set_password(password), - _ => self.add_internet_password( - server, - security_domain, - account, - path, - port, - protocol, - authentication_type, - password, - ), - } - } - - /// Set a generic password. - /// - /// * `keychain_opt` is the keychain to use or None to use the default - /// keychain. - /// * `service` is the associated service name for the password. - /// * `account` is the associated account name for the password. - /// * `password` is the password itself. - pub fn set_generic_password( - &self, - service: &str, - account: &str, - password: &[u8], - ) -> Result<()> { - match self.find_generic_password(service, account) { - Ok((_, mut item)) => item.set_password(password), - _ => self.add_generic_password(service, account, password), - } - } - - /// Add application password to the keychain, without checking if it exists already - /// - /// See `set_generic_password()` - #[inline] - pub fn add_generic_password( - &self, - service: &str, - account: &str, - password: &[u8], - ) -> Result<()> { - unsafe { - cvt(SecKeychainAddGenericPassword( - self.as_CFTypeRef() as *mut _, - service.len() as u32, - service.as_ptr().cast(), - account.len() as u32, - account.as_ptr().cast(), - password.len() as u32, - password.as_ptr().cast(), - ptr::null_mut(), - ))?; - } - Ok(()) - } - - /// Add internet password to the keychain, without checking if it exists already - /// - /// See `set_internet_password()` - #[inline] - #[allow(clippy::too_many_arguments)] - pub fn add_internet_password( - &self, - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, - password: &[u8], - ) -> Result<()> { - unsafe { - cvt(SecKeychainAddInternetPassword( - self.as_CFTypeRef() as *mut _, - server.len() as u32, - server.as_ptr().cast(), - security_domain.map_or(0, |s| s.len() as u32), - security_domain - .map_or(ptr::null(), |s| s.as_ptr().cast()), - account.len() as u32, - account.as_ptr().cast(), - path.len() as u32, - path.as_ptr().cast(), - port.unwrap_or(0), - protocol, - authentication_type, - password.len() as u32, - password.as_ptr().cast(), - ptr::null_mut(), - ))?; - } - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::os::macos::keychain::CreateOptions; - use tempfile::tempdir; - use tempfile::TempDir; - - fn temp_keychain_setup(name: &str) -> (TempDir, SecKeychain) { - let dir = tempdir().expect("TempDir::new"); - let keychain = CreateOptions::new() - .password("foobar") - .create(dir.path().join(name.to_string() + ".keychain")) - .expect("create keychain"); - - (dir, keychain) - } - - fn temp_keychain_teardown(dir: TempDir) { - dir.close().expect("temp dir close"); - } - - #[test] - fn missing_password_temp() { - let (dir, keychain) = temp_keychain_setup("missing_password"); - let keychains = vec![keychain]; - - let service = "temp_this_service_does_not_exist"; - let account = "this_account_is_bogus"; - let found = find_generic_password(Some(&keychains), service, account); - - assert!(found.is_err()); - - temp_keychain_teardown(dir); - } - - #[test] - #[cfg(feature = "default_keychain_tests")] - fn missing_password_default() { - let service = "default_this_service_does_not_exist"; - let account = "this_account_is_bogus"; - let found = find_generic_password(None, service, account); - - assert!(found.is_err()); - } - - #[test] - fn round_trip_password_temp() { - let (dir, keychain) = temp_keychain_setup("round_trip_password"); - - let service = "test_round_trip_password_temp"; - let account = "temp_this_is_the_test_account"; - let password = String::from("deadbeef").into_bytes(); - - keychain - .set_generic_password(service, account, &password) - .expect("set_generic_password"); - let (found, item) = keychain - .find_generic_password(service, account) - .expect("find_generic_password"); - assert_eq!(found.to_owned(), password); - - item.delete(); - - temp_keychain_teardown(dir); - } - - #[test] - #[cfg(feature = "default_keychain_tests")] - fn round_trip_password_default() { - let service = "test_round_trip_password_default"; - let account = "this_is_the_test_account"; - let password = String::from("deadbeef").into_bytes(); - - SecKeychain::default() - .expect("default keychain") - .set_generic_password(service, account, &password) - .expect("set_generic_password"); - let (found, item) = - find_generic_password(None, service, account).expect("find_generic_password"); - assert_eq!(&*found, &password[..]); - - item.delete(); - } - - #[test] - fn change_password_temp() { - let (dir, keychain) = temp_keychain_setup("change_password"); - let keychains = vec![keychain]; - - let service = "test_change_password_temp"; - let account = "this_is_the_test_account"; - let pw1 = String::from("password1").into_bytes(); - let pw2 = String::from("password2").into_bytes(); - - keychains[0] - .set_generic_password(service, account, &pw1) - .expect("set_generic_password1"); - let (found, _) = find_generic_password(Some(&keychains), service, account) - .expect("find_generic_password1"); - assert_eq!(found.as_ref(), &pw1[..]); - - keychains[0] - .set_generic_password(service, account, &pw2) - .expect("set_generic_password2"); - let (found, item) = find_generic_password(Some(&keychains), service, account) - .expect("find_generic_password2"); - assert_eq!(&*found, &pw2[..]); - - item.delete(); - - temp_keychain_teardown(dir); - } - - #[test] - #[cfg(feature = "default_keychain_tests")] - fn change_password_default() { - let service = "test_change_password_default"; - let account = "this_is_the_test_account"; - let pw1 = String::from("password1").into_bytes(); - let pw2 = String::from("password2").into_bytes(); - - SecKeychain::default() - .expect("default keychain") - .set_generic_password(service, account, &pw1) - .expect("set_generic_password1"); - let (found, _) = - find_generic_password(None, service, account).expect("find_generic_password1"); - assert_eq!(found.to_owned(), pw1); - - SecKeychain::default() - .expect("default keychain") - .set_generic_password(service, account, &pw2) - .expect("set_generic_password2"); - let (found, item) = - find_generic_password(None, service, account).expect("find_generic_password2"); - assert_eq!(found.to_owned(), pw2); - - item.delete(); - } - - #[test] - fn cross_keychain_corruption_temp() { - let (dir1, keychain1) = temp_keychain_setup("cross_corrupt1"); - let (dir2, keychain2) = temp_keychain_setup("cross_corrupt2"); - let keychains1 = vec![keychain1.clone()]; - let keychains2 = vec![keychain2.clone()]; - let both_keychains = vec![keychain1, keychain2]; - - let service = "temp_this_service_does_not_exist"; - let account = "this_account_is_bogus"; - let password = String::from("deadbeef").into_bytes(); - - // Make sure this password doesn't exist in either keychain. - let found = find_generic_password(Some(&both_keychains), service, account); - assert!(found.is_err()); - - // Set a password in one keychain. - keychains1[0] - .set_generic_password(service, account, &password) - .expect("set_generic_password"); - - // Make sure it's found in that keychain. - let (found, item) = find_generic_password(Some(&keychains1), service, account) - .expect("find_generic_password1"); - assert_eq!(found.to_owned(), password); - - // Make sure it's _not_ found in the other keychain. - let found = find_generic_password(Some(&keychains2), service, account); - assert!(found.is_err()); - - // Cleanup. - item.delete(); - - temp_keychain_teardown(dir1); - temp_keychain_teardown(dir2); - } -} diff --git a/vendor/security-framework/src/os/macos/secure_transport.rs b/vendor/security-framework/src/os/macos/secure_transport.rs deleted file mode 100644 index 7d01f066..00000000 --- a/vendor/security-framework/src/os/macos/secure_transport.rs +++ /dev/null @@ -1,647 +0,0 @@ -//! OSX specific extensions to Secure Transport functionality. - -use core_foundation::array::CFArray; -use core_foundation::base::TCFType; -use security_framework_sys::secure_transport::*; -use std::ptr; -use std::slice; - -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::secure_transport::{MidHandshakeSslStream, SslContext}; -use crate::{cvt, AsInner}; - -/// An extension trait adding OSX specific functionality to the `SslContext` -/// type. -pub trait SslContextExt { - /// Returns the DER encoded data specifying the parameters used for - /// Diffie-Hellman key exchange. - fn diffie_hellman_params(&self) -> Result<Option<&[u8]>>; - - /// Sets the parameters used for Diffie-Hellman key exchange, in the - /// DER format used by OpenSSL. - /// - /// If a cipher suite which uses Diffie-Hellman key exchange is selected, - /// parameters will automatically be generated if none are provided with - /// this method, but this process can take up to 30 seconds. - /// - /// This can only be called on server-side sessions. - fn set_diffie_hellman_params(&mut self, dh_params: &[u8]) -> Result<()>; - - /// Returns the certificate authorities used to validate client - /// certificates. - fn certificate_authorities(&self) -> Result<Option<Vec<SecCertificate>>>; - - /// Sets the certificate authorities used to validate client certificates, - /// replacing any that are already present. - fn set_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()>; - - /// Adds certificate authorities used to validate client certificates. - fn add_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()>; - - /// If enabled, server identity changes are allowed during renegotiation. - /// - /// It is disabled by default to protect against triple handshake attacks. - /// - /// Requires the `OSX_10_11` (or greater) feature. - #[cfg(feature = "OSX_10_11")] - fn allow_server_identity_change(&self) -> Result<bool>; - - /// If enabled, server identity changes are allowed during renegotiation. - /// - /// It is disabled by default to protect against triple handshake attacks. - /// - /// Requires the `OSX_10_11` (or greater) feature. - #[cfg(feature = "OSX_10_11")] - fn set_allow_server_identity_change(&mut self, value: bool) -> Result<()>; - - /// If enabled, fallback countermeasures will be used during negotiation. - /// - /// It should be enabled when renegotiating with a peer with a lower - /// maximum protocol version due to an earlier failure to connect. - /// - /// Requires the `OSX_10_10` (or greater) feature. - #[cfg(feature = "OSX_10_10")] - fn fallback(&self) -> Result<bool>; - - /// If enabled, fallback countermeasures will be used during negotiation. - /// - /// It should be enabled when renegotiating with a peer with a lower - /// maximum protocol version due to an earlier failure to connect. - /// - /// Requires the `OSX_10_10` (or greater) feature. - #[cfg(feature = "OSX_10_10")] - fn set_fallback(&mut self, value: bool) -> Result<()>; - - /// If enabled, the handshake process will pause and return when the client - /// hello is recieved to support server name identification. - /// - /// Requires the `OSX_10_11` (or greater) feature. - #[cfg(feature = "OSX_10_11")] - fn break_on_client_hello(&self) -> Result<bool>; - - /// If enabled, the handshake process will pause and return when the client - /// hello is recieved to support server name identification. - /// - /// Requires the `OSX_10_11` (or greater) feature. - #[cfg(feature = "OSX_10_11")] - fn set_break_on_client_hello(&mut self, value: bool) -> Result<()>; -} - -macro_rules! impl_options { - ($($(#[$a:meta])* const $opt:ident: $get:ident & $set:ident,)*) => { - $( - $(#[$a])* - #[inline] - fn $set(&mut self, value: bool) -> Result<()> { - unsafe { - cvt(SSLSetSessionOption(self.as_inner(), - $opt, - value as ::core_foundation::base::Boolean)) - } - } - - $(#[$a])* - #[inline] - fn $get(&self) -> Result<bool> { - let mut value = 0; - unsafe { cvt(SSLGetSessionOption(self.as_inner(), $opt, &mut value))?; } - Ok(value != 0) - } - )* - } -} - -impl SslContextExt for SslContext { - fn diffie_hellman_params(&self) -> Result<Option<&[u8]>> { - unsafe { - let mut ptr = ptr::null(); - let mut len = 0; - cvt(SSLGetDiffieHellmanParams( - self.as_inner(), - &mut ptr, - &mut len, - ))?; - if ptr.is_null() { - Ok(None) - } else { - Ok(Some(slice::from_raw_parts(ptr.cast::<u8>(), len))) - } - } - } - - fn set_diffie_hellman_params(&mut self, dh_params: &[u8]) -> Result<()> { - unsafe { - cvt(SSLSetDiffieHellmanParams( - self.as_inner(), - dh_params.as_ptr().cast(), - dh_params.len(), - )) - } - } - - fn certificate_authorities(&self) -> Result<Option<Vec<SecCertificate>>> { - unsafe { - let mut raw_certs = ptr::null(); - cvt(SSLCopyCertificateAuthorities( - self.as_inner(), - &mut raw_certs, - ))?; - if raw_certs.is_null() { - Ok(None) - } else { - let certs = CFArray::<SecCertificate>::wrap_under_create_rule(raw_certs) - .iter() - .map(|c| c.clone()) - .collect(); - Ok(Some(certs)) - } - } - } - - fn set_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()> { - unsafe { - let certs = CFArray::from_CFTypes(certs); - cvt(SSLSetCertificateAuthorities( - self.as_inner(), - certs.as_CFTypeRef(), - 1, - )) - } - } - - fn add_certificate_authorities(&mut self, certs: &[SecCertificate]) -> Result<()> { - unsafe { - let certs = CFArray::from_CFTypes(certs); - cvt(SSLSetCertificateAuthorities( - self.as_inner(), - certs.as_CFTypeRef(), - 0, - )) - } - } - - impl_options! { - #[cfg(feature = "OSX_10_11")] - const kSSLSessionOptionAllowServerIdentityChange: allow_server_identity_change & set_allow_server_identity_change, - #[cfg(feature = "OSX_10_10")] - const kSSLSessionOptionFallback: fallback & set_fallback, - #[cfg(feature = "OSX_10_11")] - const kSSLSessionOptionBreakOnClientHello: break_on_client_hello & set_break_on_client_hello, - } -} - -/// An extension trait adding OSX specific functionality to the -/// `MidHandshakeSslStream` type. -pub trait MidHandshakeSslStreamExt { - /// Returns `true` iff `break_on_client_hello` was set and the handshake - /// has progressed to that point. - /// - /// Requires the `OSX_10_11` (or greater) feature. - #[cfg(feature = "OSX_10_11")] - fn client_hello_received(&self) -> bool; -} - -impl<S> MidHandshakeSslStreamExt for MidHandshakeSslStream<S> { - #[cfg(feature = "OSX_10_11")] - fn client_hello_received(&self) -> bool { - self.error().code() == errSSLClientHelloReceived - } -} - -#[cfg(test)] -mod test { - use std::io::prelude::*; - use std::net::{TcpListener, TcpStream}; - use std::thread; - use tempfile::tempdir; - - use super::*; - use crate::cipher_suite::CipherSuite; - use crate::os::macos::test::identity; - use crate::secure_transport::*; - use crate::test::certificate; - - #[test] - fn server_client() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - - let stream = p!(listener.accept()).0; - let mut stream = p!(ctx.handshake(stream)); - - let mut buf = [0; 12]; - p!(stream.read(&mut buf)); - assert_eq!(&buf[..], b"hello world!"); - }); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_break_on_server_auth(true)); - let stream = p!(TcpStream::connect(("localhost", port))); - - let stream = match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Interrupted(stream)) => stream, - Err(err) => panic!("unexpected error {err:?}"), - }; - - assert!(stream.server_auth_completed()); - let mut peer_trust = p!(stream.context().peer_trust2()).unwrap(); - p!(peer_trust.set_anchor_certificates(&[certificate()])); - p!(peer_trust.evaluate_with_error()); - - let mut stream = p!(stream.handshake()); - p!(stream.write_all(b"hello world!")); - - handle.join().unwrap(); - } - - #[test] - #[ignore] - fn server_client_builders() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let identity = identity(dir.path()); - let builder = ServerBuilder::new(&identity, &[]); - - let stream = p!(listener.accept()).0; - let mut stream = p!(builder.handshake(stream)); - - let mut buf = [0; 12]; - p!(stream.read(&mut buf)); - assert_eq!(&buf[..], b"hello world!"); - }); - - let stream = p!(TcpStream::connect(("localhost", port))); - let mut stream = p!(ClientBuilder::new() - .anchor_certificates(&[certificate()]) - .handshake("foobar.com", stream)); - - p!(stream.write_all(b"hello world!")); - - handle.join().unwrap(); - } - - #[test] - fn client_bad_cert() { - let _ = env_logger::try_init(); - - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - - let stream = p!(listener.accept()).0; - let _ = ctx.handshake(stream); - }); - - let stream = p!(TcpStream::connect(("localhost", port))); - assert!(ClientBuilder::new() - .handshake("foobar.com", stream) - .is_err()); - - handle.join().unwrap(); - } - - #[test] - #[ignore] - fn client() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - - let stream = p!(listener.accept()).0; - let mut stream = p!(ctx.handshake(stream)); - - let mut buf = [0; 12]; - p!(stream.read(&mut buf)); - assert_eq!(&buf[..], b"hello world!"); - }); - - let stream = p!(TcpStream::connect(("localhost", port))); - let mut stream = p!(ClientBuilder::new() - .anchor_certificates(&[certificate()]) - .handshake("foobar.com", stream)); - p!(stream.write_all(b"hello world!")); - - handle.join().unwrap(); - } - - #[test] - fn negotiated_cipher() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - p!(ctx.set_enabled_ciphers(&[ - CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ])); - - let stream = p!(listener.accept()).0; - let mut stream = p!(ctx.handshake(stream)); - assert_eq!( - CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - p!(stream.context().negotiated_cipher()) - ); - let mut buf = [0; 1]; - p!(stream.read(&mut buf)); - }); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_break_on_server_auth(true)); - p!(ctx.set_enabled_ciphers(&[ - CipherSuite::TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, - CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - ])); - let stream = p!(TcpStream::connect(("localhost", port))); - - let stream = match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Interrupted(stream)) => stream, - Err(err) => panic!("unexpected error {err:?}"), - }; - - let mut stream = p!(stream.handshake()); - assert_eq!( - CipherSuite::TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - p!(stream.context().negotiated_cipher()) - ); - p!(stream.write(&[0])); - - handle.join().unwrap(); - } - - #[test] - fn dh_params() { - let params = include_bytes!("../../../test/dhparam.der"); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - assert!(p!(ctx.diffie_hellman_params()).is_none()); - p!(ctx.set_diffie_hellman_params(params)); - assert_eq!(p!(ctx.diffie_hellman_params()).unwrap(), ¶ms[..]); - } - - #[test] - fn try_authenticate_no_cert() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - p!(ctx.set_client_side_authenticate(SslAuthenticate::TRY)); - let cert = certificate(); - p!(ctx.add_certificate_authorities(&[cert])); - - let stream = p!(listener.accept()).0; - let mut stream = p!(ctx.handshake(stream)); - let mut buf = [0; 1]; - p!(stream.read(&mut buf)); - }); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_break_on_server_auth(true)); - let stream = p!(TcpStream::connect(("localhost", port))); - - let stream = match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Interrupted(stream)) => stream, - Err(err) => panic!("unexpected error {err:?}"), - }; - - let mut stream = p!(stream.handshake()); - p!(stream.write(&[0])); - - handle.join().unwrap(); - } - - #[test] - fn always_authenticate_no_cert() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - p!(ctx.set_client_side_authenticate(SslAuthenticate::ALWAYS)); - - let stream = p!(listener.accept()).0; - - match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Failure(_)) => {} - Err(err) => panic!("unexpected error {err:?}"), - } - }); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_break_on_server_auth(true)); - let stream = p!(TcpStream::connect(("localhost", port))); - - let stream = match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Interrupted(stream)) => stream, - Err(err) => panic!("unexpected error {err:?}"), - }; - - match stream.handshake() { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Failure(_)) => {} - Err(err) => panic!("unexpected error {err:?}"), - } - - handle.join().unwrap(); - } - - #[test] - fn always_authenticate_with_cert() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - p!(ctx.set_client_side_authenticate(SslAuthenticate::ALWAYS)); - - let stream = p!(listener.accept()).0; - - match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Failure(_)) => {} - Err(err) => panic!("unexpected error {err:?}"), - } - }); - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_break_on_server_auth(true)); - let dir = p!(tempdir()); - let identity = identity(dir.path()); - p!(ctx.set_certificate(&identity, &[])); - let stream = p!(TcpStream::connect(("localhost", port))); - - let stream = match ctx.handshake(stream) { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Interrupted(stream)) => stream, - Err(err) => panic!("unexpected error {err:?}"), - }; - - match stream.handshake() { - Ok(_) => panic!("unexpected success"), - Err(HandshakeError::Failure(_)) => {} - Err(err) => panic!("unexpected error {err:?}"), - } - - handle.join().unwrap(); - } - - #[test] - fn certificate_authorities() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - assert!(p!(ctx.certificate_authorities()).is_none()); - p!(ctx.set_certificate_authorities(&[certificate()])); - assert_eq!(p!(ctx.certificate_authorities()).unwrap().len(), 1); - } - - #[test] - #[ignore] - fn close() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let identity = identity(dir.path()); - let builder = ServerBuilder::new(&identity, &[]); - - let stream = p!(listener.accept()).0; - let mut stream = p!(builder.handshake(stream)); - p!(stream.close()); - }); - - let stream = p!(TcpStream::connect(("localhost", port))); - let mut stream = p!(ClientBuilder::new() - .anchor_certificates(&[certificate()]) - .handshake("foobar.com", stream)); - - let mut buf = [0; 1]; - assert_eq!(p!(stream.read(&mut buf)), 0); - p!(stream.close()); - - p!(handle.join()); - } - - #[test] - #[ignore] - fn short_read() { - let listener = p!(TcpListener::bind("localhost:0")); - let port = p!(listener.local_addr()).port(); - - let handle = thread::spawn(move || { - let dir = p!(tempdir()); - - let identity = identity(dir.path()); - let builder = ServerBuilder::new(&identity, &[]); - - let stream = p!(listener.accept()).0; - let mut stream = p!(builder.handshake(stream)); - - stream.write_all(b"hello").unwrap(); - // make sure stream doesn't close - stream - }); - - let stream = p!(TcpStream::connect(("localhost", port))); - let mut stream = p!(ClientBuilder::new() - .anchor_certificates(&[certificate()]) - .handshake("foobar.com", stream)); - - let mut b = [0; 1]; - stream.read_exact(&mut b).unwrap(); - assert_eq!(stream.context().buffered_read_size().unwrap(), 4); - let mut b = [0; 5]; - let read = stream.read(&mut b).unwrap(); - assert_eq!(read, 4); - - p!(handle.join()); - } -} diff --git a/vendor/security-framework/src/os/macos/transform.rs b/vendor/security-framework/src/os/macos/transform.rs deleted file mode 100644 index d03bc1f7..00000000 --- a/vendor/security-framework/src/os/macos/transform.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Transform support - -use core_foundation::base::{CFType, TCFType}; -use core_foundation::error::CFError; -use core_foundation::string::CFString; -use security_framework_sys::transform::*; -use std::ptr; - -declare_TCFType! { - /// A type representing a transform. - SecTransform, SecTransformRef -} -impl_TCFType!(SecTransform, SecTransformRef, SecTransformGetTypeID); - -unsafe impl Sync for SecTransform {} -unsafe impl Send for SecTransform {} - -impl SecTransform { - /// Sets an attribute of the transform. - pub fn set_attribute<T>(&mut self, key: &CFString, value: &T) -> Result<(), CFError> - where - T: TCFType, - { - unsafe { - let mut error = ptr::null_mut(); - SecTransformSetAttribute( - self.0, - key.as_concrete_TypeRef(), - value.as_CFTypeRef(), - &mut error, - ); - if !error.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - - Ok(()) - } - } - - /// Executes the transform. - /// - /// The return type depends on the type of transform. - pub fn execute(&mut self) -> Result<CFType, CFError> { - unsafe { - let mut error = ptr::null_mut(); - let result = SecTransformExecute(self.0, &mut error); - if result.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - - Ok(CFType::wrap_under_create_rule(result)) - } - } -} diff --git a/vendor/security-framework/src/os/mod.rs b/vendor/security-framework/src/os/mod.rs deleted file mode 100644 index ec7e38b3..00000000 --- a/vendor/security-framework/src/os/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! OS specific extensions. - -#[cfg(target_os = "macos")] -pub mod macos; diff --git a/vendor/security-framework/src/passwords.rs b/vendor/security-framework/src/passwords.rs deleted file mode 100644 index 0e83f432..00000000 --- a/vendor/security-framework/src/passwords.rs +++ /dev/null @@ -1,337 +0,0 @@ -//! Support for password entries in the keychain. Works on both iOS and macOS. -//! -//! If you want the extended keychain facilities only available on macOS, use the -//! version of these functions in the macOS extensions module. - -use crate::base::Result; -use crate::passwords_options::PasswordOptions; -use crate::{cvt, Error}; -use core_foundation::base::TCFType; -use core_foundation::boolean::CFBoolean; -use core_foundation::data::CFData; -use core_foundation::dictionary::CFDictionary; -use core_foundation::string::CFString; -use core_foundation_sys::base::{CFGetTypeID, CFRelease, CFTypeRef}; -use core_foundation_sys::data::CFDataRef; -use security_framework_sys::base::{errSecDuplicateItem, errSecParam}; -use security_framework_sys::item::{kSecReturnData, kSecValueData}; -use security_framework_sys::keychain::{SecAuthenticationType, SecProtocolType}; -use security_framework_sys::keychain_item::{ - SecItemAdd, SecItemCopyMatching, SecItemDelete, SecItemUpdate, -}; - -/// Set a generic password for the given service and account. -/// Creates or updates a keychain entry. -pub fn set_generic_password(service: &str, account: &str, password: &[u8]) -> Result<()> { - let mut options = PasswordOptions::new_generic_password(service, account); - set_password_internal(&mut options, password) -} - -/// Get the generic password for the given service and account. If no matching -/// keychain entry exists, fails with error code `errSecItemNotFound`. -pub fn get_generic_password(service: &str, account: &str) -> Result<Vec<u8>> { - let mut options = PasswordOptions::new_generic_password(service, account); - options.query.push(( - unsafe { CFString::wrap_under_get_rule(kSecReturnData) }, - CFBoolean::from(true).into_CFType(), - )); - let params = CFDictionary::from_CFType_pairs(&options.query); - let mut ret: CFTypeRef = std::ptr::null(); - cvt(unsafe { SecItemCopyMatching(params.as_concrete_TypeRef(), &mut ret) })?; - get_password_and_release(ret) -} - -/// Delete the generic password keychain entry for the given service and account. -/// If none exists, fails with error code `errSecItemNotFound`. -pub fn delete_generic_password(service: &str, account: &str) -> Result<()> { - let options = PasswordOptions::new_generic_password(service, account); - let params = CFDictionary::from_CFType_pairs(&options.query); - cvt(unsafe { SecItemDelete(params.as_concrete_TypeRef()) }) -} - -/// Set an internet password for the given endpoint parameters. -/// Creates or updates a keychain entry. -#[allow(clippy::too_many_arguments)] -pub fn set_internet_password( - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, - password: &[u8], -) -> Result<()> { - let mut options = PasswordOptions::new_internet_password( - server, - security_domain, - account, - path, - port, - protocol, - authentication_type, - ); - set_password_internal(&mut options, password) -} - -/// Get the internet password for the given endpoint parameters. If no matching -/// keychain entry exists, fails with error code `errSecItemNotFound`. -pub fn get_internet_password( - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, -) -> Result<Vec<u8>> { - let mut options = PasswordOptions::new_internet_password( - server, - security_domain, - account, - path, - port, - protocol, - authentication_type, - ); - options.query.push(( - unsafe { CFString::wrap_under_get_rule(kSecReturnData) }, - CFBoolean::from(true).into_CFType(), - )); - let params = CFDictionary::from_CFType_pairs(&options.query); - let mut ret: CFTypeRef = std::ptr::null(); - cvt(unsafe { SecItemCopyMatching(params.as_concrete_TypeRef(), &mut ret) })?; - get_password_and_release(ret) -} - -/// Delete the internet password for the given endpoint parameters. -/// If none exists, fails with error code `errSecItemNotFound`. -pub fn delete_internet_password( - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, -) -> Result<()> { - let options = PasswordOptions::new_internet_password( - server, - security_domain, - account, - path, - port, - protocol, - authentication_type, - ); - let params = CFDictionary::from_CFType_pairs(&options.query); - cvt(unsafe { SecItemDelete(params.as_concrete_TypeRef()) }) -} - -// This starts by trying to create the password with the given query params. -// If the creation attempt reveals that one exists, its password is updated. -fn set_password_internal(options: &mut PasswordOptions, password: &[u8]) -> Result<()> { - let query_len = options.query.len(); - options.query.push(( - unsafe { CFString::wrap_under_get_rule(kSecValueData) }, - CFData::from_buffer(password).into_CFType(), - )); - - let params = CFDictionary::from_CFType_pairs(&options.query); - let mut ret = std::ptr::null(); - let status = unsafe { SecItemAdd(params.as_concrete_TypeRef(), &mut ret) }; - if status == errSecDuplicateItem { - let params = CFDictionary::from_CFType_pairs(&options.query[0..query_len]); - let update = CFDictionary::from_CFType_pairs(&options.query[query_len..]); - cvt(unsafe { SecItemUpdate(params.as_concrete_TypeRef(), update.as_concrete_TypeRef()) }) - } else { - cvt(status) - } -} - -// Having retrieved a password entry, this copies and returns the password. -// -// # Safety -// The data element passed in is assumed to have been returned from a Copy -// call, so it's released after we are done with it. -fn get_password_and_release(data: CFTypeRef) -> Result<Vec<u8>> { - if !data.is_null() { - let type_id = unsafe { CFGetTypeID(data) }; - if type_id == CFData::type_id() { - let val = unsafe { CFData::wrap_under_create_rule(data as CFDataRef) }; - let mut vec = Vec::new(); - if val.len() > 0 { - vec.extend_from_slice(val.bytes()); - } - return Ok(vec); - } - // unexpected: we got a reference to some other type. - // Release it to make sure there's no leak, but - // we can't return the password in this case. - unsafe { CFRelease(data) }; - } - Err(Error::from_code(errSecParam)) -} - -#[cfg(test)] -mod test { - use super::*; - use security_framework_sys::base::errSecItemNotFound; - - #[test] - fn missing_generic() { - let name = "a string not likely to already be in the keychain as service or account"; - let result = delete_generic_password(name, name); - match result { - Ok(()) => (), // this is ok because the name _might_ be in the keychain - Err(err) if err.code() == errSecItemNotFound => (), - Err(err) => panic!("missing_generic: delete failed with status: {}", err.code()), - }; - let result = get_generic_password(name, name); - match result { - Ok(bytes) => panic!("missing_generic: get returned {bytes:?}"), - Err(err) if err.code() == errSecItemNotFound => (), - Err(err) => panic!("missing_generic: get failed with status: {}", err.code()), - }; - let result = delete_generic_password(name, name); - match result { - Ok(()) => panic!("missing_generic: second delete found a password"), - Err(err) if err.code() == errSecItemNotFound => (), - Err(err) => panic!("missing_generic: delete failed with status: {}", err.code()), - }; - } - - #[test] - fn roundtrip_generic() { - let name = "roundtrip_generic"; - set_generic_password(name, name, name.as_bytes()).expect("set_generic_password"); - let pass = get_generic_password(name, name).expect("get_generic_password"); - assert_eq!(name.as_bytes(), pass); - delete_generic_password(name, name).expect("delete_generic_password"); - } - - #[test] - fn update_generic() { - let name = "update_generic"; - set_generic_password(name, name, name.as_bytes()).expect("set_generic_password"); - let alternate = "update_generic_alternate"; - set_generic_password(name, name, alternate.as_bytes()).expect("set_generic_password"); - let pass = get_generic_password(name, name).expect("get_generic_password"); - assert_eq!(pass, alternate.as_bytes()); - delete_generic_password(name, name).expect("delete_generic_password"); - } - - #[test] - fn missing_internet() { - let name = "a string not likely to already be in the keychain as service or account"; - let (server, domain, account, path, port, protocol, auth) = ( - name, - None, - name, - "/", - Some(8080u16), - SecProtocolType::HTTP, - SecAuthenticationType::Any, - ); - let result = delete_internet_password(server, domain, account, path, port, protocol, auth); - match result { - Ok(()) => (), // this is ok because the name _might_ be in the keychain - Err(err) if err.code() == errSecItemNotFound => (), - Err(err) => panic!( - "missing_internet: delete failed with status: {}", - err.code() - ), - }; - let result = get_internet_password(server, domain, account, path, port, protocol, auth); - match result { - Ok(bytes) => panic!("missing_internet: get returned {bytes:?}"), - Err(err) if err.code() == errSecItemNotFound => (), - Err(err) => panic!("missing_internet: get failed with status: {}", err.code()), - }; - let result = delete_internet_password(server, domain, account, path, port, protocol, auth); - match result { - Ok(()) => panic!("missing_internet: second delete found a password"), - Err(err) if err.code() == errSecItemNotFound => (), - Err(err) => panic!( - "missing_internet: delete failed with status: {}", - err.code() - ), - }; - } - - #[test] - fn roundtrip_internet() { - let name = "roundtrip_internet"; - let (server, domain, account, path, port, protocol, auth) = ( - name, - None, - name, - "/", - Some(8080u16), - SecProtocolType::HTTP, - SecAuthenticationType::Any, - ); - set_internet_password( - server, - domain, - account, - path, - port, - protocol, - auth, - name.as_bytes(), - ) - .expect("set_internet_password"); - let pass = get_internet_password(server, domain, account, path, port, protocol, auth) - .expect("get_internet_password"); - assert_eq!(name.as_bytes(), pass); - delete_internet_password(server, domain, account, path, port, protocol, auth) - .expect("delete_internet_password"); - } - - #[test] - fn update_internet() { - let name = "update_internet"; - let (server, domain, account, path, port, protocol, auth) = ( - name, - None, - name, - "/", - Some(8080u16), - SecProtocolType::HTTP, - SecAuthenticationType::Any, - ); - - // cleanup after failed test - let _ = delete_internet_password(server, domain, account, path, port, protocol, auth); - - set_internet_password( - server, - domain, - account, - path, - port, - protocol, - auth, - name.as_bytes(), - ) - .expect("set_internet_password"); - let alternate = "alternate_internet_password"; - set_internet_password( - server, - domain, - account, - path, - port, - protocol, - auth, - alternate.as_bytes(), - ) - .expect("set_internet_password"); - let pass = get_internet_password(server, domain, account, path, port, protocol, auth) - .expect("get_internet_password"); - assert_eq!(pass, alternate.as_bytes()); - delete_internet_password(server, domain, account, path, port, protocol, auth) - .expect("delete_internet_password"); - } -} diff --git a/vendor/security-framework/src/passwords_options.rs b/vendor/security-framework/src/passwords_options.rs deleted file mode 100644 index 7d85d01c..00000000 --- a/vendor/security-framework/src/passwords_options.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! Support for password options, to be used with the passwords module - -use core_foundation::{string::CFString, base::{CFType, TCFType, CFOptionFlags}, number::CFNumber}; -use security_framework_sys::{keychain::{SecProtocolType, SecAuthenticationType}, access_control::*}; -use security_framework_sys::item::{ - kSecAttrAccessControl, kSecAttrAccount, kSecAttrAuthenticationType, kSecAttrPath, kSecAttrPort, kSecAttrProtocol, - kSecAttrSecurityDomain, kSecAttrServer, kSecAttrService, kSecClass, kSecClassGenericPassword, - kSecClassInternetPassword, -}; -use crate::access_control::SecAccessControl; - -/// `PasswordOptions` constructor -pub struct PasswordOptions { - /// query built for the keychain request - pub query: Vec<(CFString, CFType)>, -} - -bitflags::bitflags! { - /// The option flags used to configure the evaluation of a `SecAccessControl`. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct AccessControlOptions: CFOptionFlags { - /** Constraint to access an item with either biometry or passcode. */ - const USER_PRESENCE = kSecAccessControlUserPresence; - #[cfg(feature = "OSX_10_13")] - /** Constraint to access an item with Touch ID for any enrolled fingers, or Face ID. */ - const BIOMETRY_ANY = kSecAccessControlBiometryAny; - #[cfg(feature = "OSX_10_13")] - /** Constraint to access an item with Touch ID for currently enrolled fingers, or from Face ID with the currently enrolled user. */ - const BIOMETRY_CURRENT_SET = kSecAccessControlBiometryCurrentSet; - /** Constraint to access an item with a passcode. */ - const DEVICE_PASSCODE = kSecAccessControlDevicePasscode; - #[cfg(feature = "OSX_10_15")] - /** Constraint to access an item with a watch. */ - const WATCH = kSecAccessControlWatch; - /** Indicates that at least one constraint must be satisfied. */ - const OR = kSecAccessControlOr; - /** Indicates that all constraints must be satisfied. */ - const AND = kSecAccessControlAnd; - /** Enable a private key to be used in signing a block of data or verifying a signed block. */ - const PRIVATE_KEY_USAGE = kSecAccessControlPrivateKeyUsage; - /** Option to use an application-provided password for data encryption key generation. */ - const APPLICATION_PASSWORD = kSecAccessControlApplicationPassword; - } -} - -impl PasswordOptions { - /// Create a new generic password options - /// Generic passwords are identified by service and account. They have other - /// attributes, but this interface doesn't allow specifying them. - #[must_use] pub fn new_generic_password(service: &str, account: &str) -> Self { - let query = vec![ - ( - unsafe { CFString::wrap_under_get_rule(kSecClass) }, - unsafe { CFString::wrap_under_get_rule(kSecClassGenericPassword).into_CFType() }, - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrService) }, - CFString::from(service).into_CFType(), - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, - CFString::from(account).into_CFType(), - ), - ]; - Self { query } - } - - /// Create a new internet password options - /// Internet passwords are identified by a number of attributes. - /// They can have others, but this interface doesn't allow specifying them. - #[must_use] pub fn new_internet_password( - server: &str, - security_domain: Option<&str>, - account: &str, - path: &str, - port: Option<u16>, - protocol: SecProtocolType, - authentication_type: SecAuthenticationType, - ) -> Self { - let mut query = vec![ - ( - unsafe { CFString::wrap_under_get_rule(kSecClass) }, - unsafe { CFString::wrap_under_get_rule(kSecClassInternetPassword) }.into_CFType(), - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrServer) }, - CFString::from(server).into_CFType(), - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrPath) }, - CFString::from(path).into_CFType(), - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, - CFString::from(account).into_CFType(), - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrProtocol) }, - CFNumber::from(protocol as i32).into_CFType(), - ), - ( - unsafe { CFString::wrap_under_get_rule(kSecAttrAuthenticationType) }, - CFNumber::from(authentication_type as i32).into_CFType(), - ), - ]; - if let Some(domain) = security_domain { - query.push(( - unsafe { CFString::wrap_under_get_rule(kSecAttrSecurityDomain) }, - CFString::from(domain).into_CFType(), - )); - } - if let Some(port) = port { - query.push(( - unsafe { CFString::wrap_under_get_rule(kSecAttrPort) }, - CFNumber::from(i32::from(port)).into_CFType(), - )); - } - Self { query } - } - - /// Add access control to the password - pub fn set_access_control_options(&mut self, options: AccessControlOptions) { - self.query.push(( - unsafe { CFString::wrap_under_get_rule(kSecAttrAccessControl) }, - SecAccessControl::create_with_flags(options.bits()) - .unwrap() - .into_CFType(), - )); - } -} diff --git a/vendor/security-framework/src/policy.rs b/vendor/security-framework/src/policy.rs deleted file mode 100644 index f70a97c5..00000000 --- a/vendor/security-framework/src/policy.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Security Policies support. -use core_foundation::base::CFOptionFlags; -use core_foundation::base::TCFType; -use core_foundation::string::CFString; -use security_framework_sys::base::errSecParam; -use security_framework_sys::base::SecPolicyRef; -use security_framework_sys::policy::*; -use std::fmt; -use std::ptr; - -use crate::secure_transport::SslProtocolSide; -use crate::Error; - -declare_TCFType! { - /// A type representing a certificate validation policy. - SecPolicy, SecPolicyRef -} -impl_TCFType!(SecPolicy, SecPolicyRef, SecPolicyGetTypeID); - -unsafe impl Sync for SecPolicy {} -unsafe impl Send for SecPolicy {} - -impl fmt::Debug for SecPolicy { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("SecPolicy").finish() - } -} - -bitflags::bitflags! { - /// The flags used to specify revocation policy options. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct RevocationPolicy: CFOptionFlags { - /// Perform revocation checking using OCSP (Online Certificate Status Protocol). - const OCSP_METHOD = kSecRevocationOCSPMethod; - /// Perform revocation checking using the CRL (Certification Revocation List) method. - const CRL_METHOD = kSecRevocationCRLMethod; - /// Prefer CRL revocation checking over OCSP; by default, OCSP is preferred. - const PREFER_CRL = kSecRevocationPreferCRL; - /// Require a positive response to pass the policy. - const REQUIRE_POSITIVE_RESPONSE = kSecRevocationRequirePositiveResponse; - /// Consult only locally cached replies; do not use network access. - const NETWORK_ACCESS_DISABLED = kSecRevocationNetworkAccessDisabled; - /// Perform either OCSP or CRL checking. - const USE_ANY_METHOD_AVAILABLE = kSecRevocationUseAnyAvailableMethod; - } -} - -impl SecPolicy { - /// Creates a `SecPolicy` for evaluating SSL certificate chains. - /// - /// The side which you are evaluating should be provided (i.e. pass `SslSslProtocolSide::SERVER` if - /// you are a client looking to validate a server's certificate chain). - pub fn create_ssl(protocol_side: SslProtocolSide, hostname: Option<&str>) -> Self { - let hostname = hostname.map(CFString::new); - let hostname = hostname - .as_ref() - .map(|s| s.as_concrete_TypeRef()) - .unwrap_or(ptr::null_mut()); - let is_server = protocol_side == SslProtocolSide::SERVER; - unsafe { - let policy = SecPolicyCreateSSL(is_server as _, hostname); - Self::wrap_under_create_rule(policy) - } - } - - /// Creates a `SecPolicy` for checking revocation of certificates. - /// - /// If you do not specify this policy creating a `SecTrust` object, the system defaults - /// will be used during evaluation. - pub fn create_revocation(options: RevocationPolicy) -> crate::Result<Self> { - let policy = unsafe { SecPolicyCreateRevocation(options.bits()) }; - - if policy.is_null() { - Err(Error::from_code(errSecParam)) - } else { - Ok(unsafe { Self::wrap_under_create_rule(policy) }) - } - } - - /// Returns a policy object for the default X.509 policy. - #[must_use] - pub fn create_x509() -> Self { - unsafe { - let policy = SecPolicyCreateBasicX509(); - Self::wrap_under_create_rule(policy) - } - } -} - -#[cfg(test)] -mod test { - use crate::policy::SecPolicy; - use crate::secure_transport::SslProtocolSide; - - #[test] - fn create_ssl() { - SecPolicy::create_ssl(SslProtocolSide::SERVER, Some("certifi.org")); - } -} diff --git a/vendor/security-framework/src/random.rs b/vendor/security-framework/src/random.rs deleted file mode 100644 index 7bd7f614..00000000 --- a/vendor/security-framework/src/random.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Randomness support. - -use security_framework_sys::random::{SecRandomCopyBytes, SecRandomRef, kSecRandomDefault}; -use std::io; - -/// A source of random data. -pub struct SecRandom(SecRandomRef); - -unsafe impl Sync for SecRandom {} -unsafe impl Send for SecRandom {} - -impl Default for SecRandom { - #[inline(always)] - fn default() -> Self { - unsafe { Self(kSecRandomDefault) } - } -} - -impl SecRandom { - /// Fills the buffer with cryptographically secure random bytes. - pub fn copy_bytes(&self, buf: &mut [u8]) -> io::Result<()> { - if unsafe { SecRandomCopyBytes(self.0, buf.len(), buf.as_mut_ptr().cast()) } == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn basic() { - let mut buf = [0; 10]; - SecRandom::default().copy_bytes(&mut buf).unwrap(); - } -} diff --git a/vendor/security-framework/src/secure_transport.rs b/vendor/security-framework/src/secure_transport.rs deleted file mode 100644 index a4e7cb7f..00000000 --- a/vendor/security-framework/src/secure_transport.rs +++ /dev/null @@ -1,1843 +0,0 @@ -//! SSL/TLS encryption support using Secure Transport. -//! -//! # Examples -//! -//! To connect as a client to a server with a certificate trusted by the system: -//! -//! ```rust -//! use std::io::prelude::*; -//! use std::net::TcpStream; -//! use security_framework::secure_transport::ClientBuilder; -//! -//! let stream = TcpStream::connect("google.com:443").unwrap(); -//! let mut stream = ClientBuilder::new().handshake("google.com", stream).unwrap(); -//! -//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); -//! let mut page = vec![]; -//! stream.read_to_end(&mut page).unwrap(); -//! println!("{}", String::from_utf8_lossy(&page)); -//! ``` -//! -//! To connect to a server with a certificate that's *not* trusted by the -//! system, specify the root certificates for the server's chain to the -//! `ClientBuilder`: -//! -//! ```rust,no_run -//! use std::io::prelude::*; -//! use std::net::TcpStream; -//! use security_framework::secure_transport::ClientBuilder; -//! -//! # let root_cert = unsafe { std::mem::zeroed() }; -//! let stream = TcpStream::connect("my_server.com:443").unwrap(); -//! let mut stream = ClientBuilder::new() -//! .anchor_certificates(&[root_cert]) -//! .handshake("my_server.com", stream) -//! .unwrap(); -//! -//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); -//! let mut page = vec![]; -//! stream.read_to_end(&mut page).unwrap(); -//! println!("{}", String::from_utf8_lossy(&page)); -//! ``` -//! -//! For more advanced configuration, the `SslContext` type can be used directly. -//! -//! To run a server: -//! -//! ```rust,no_run -//! use std::net::TcpListener; -//! use std::thread; -//! use security_framework::secure_transport::{SslContext, SslProtocolSide, SslConnectionType}; -//! -//! // Create a TCP listener and start accepting on it. -//! let mut listener = TcpListener::bind("0.0.0.0:443").unwrap(); -//! -//! for stream in listener.incoming() { -//! let stream = stream.unwrap(); -//! thread::spawn(move || { -//! // Create a new context configured to operate on the server side of -//! // a traditional SSL/TLS session. -//! let mut ctx = SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM) -//! .unwrap(); -//! -//! // Install the certificate chain that we will be using. -//! # let identity = unsafe { std::mem::zeroed() }; -//! # let intermediate_cert = unsafe { std::mem::zeroed() }; -//! # let root_cert = unsafe { std::mem::zeroed() }; -//! ctx.set_certificate(identity, &[intermediate_cert, root_cert]).unwrap(); -//! -//! // Perform the SSL/TLS handshake and get our stream. -//! let mut stream = ctx.handshake(stream).unwrap(); -//! }); -//! } -//! -//! ``` -#[allow(unused_imports)] -use core_foundation::array::{CFArray, CFArrayRef}; - -use core_foundation::base::{Boolean, TCFType}; -#[cfg(feature = "alpn")] -use core_foundation::string::CFString; -use core_foundation_sys::base::{kCFAllocatorDefault, OSStatus}; -use std::os::raw::c_void; - -#[allow(unused_imports)] -use security_framework_sys::base::{ - errSecBadReq, errSecIO, errSecNotTrusted, errSecSuccess, errSecTrustSettingDeny, - errSecUnimplemented, -}; - -use security_framework_sys::secure_transport::*; -use std::any::Any; -use std::cmp; -use std::fmt; -use std::io; -use std::io::prelude::*; -use std::marker::PhantomData; -use std::panic::{self, AssertUnwindSafe}; -use std::ptr; -use std::result; -use std::slice; - -use crate::base::{Error, Result}; -use crate::certificate::SecCertificate; -use crate::cipher_suite::CipherSuite; -use crate::identity::SecIdentity; -use crate::import_export::Pkcs12ImportOptions; -use crate::policy::SecPolicy; -use crate::trust::SecTrust; -use crate::{cvt, AsInner}; -use security_framework_sys::base::errSecParam; - -/// Specifies a side of a TLS session. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SslProtocolSide(SSLProtocolSide); - -impl SslProtocolSide { - /// The server side of the session. - pub const SERVER: Self = Self(kSSLServerSide); - - /// The client side of the session. - pub const CLIENT: Self = Self(kSSLClientSide); -} - -/// Specifies the type of TLS session. -#[derive(Debug, Copy, Clone)] -pub struct SslConnectionType(SSLConnectionType); - -impl SslConnectionType { - /// A traditional TLS stream. - pub const STREAM: Self = Self(kSSLStreamType); - - /// A DTLS session. - pub const DATAGRAM: Self = Self(kSSLDatagramType); -} - -/// An error or intermediate state after a TLS handshake attempt. -#[derive(Debug)] -pub enum HandshakeError<S> { - /// The handshake failed. - Failure(Error), - /// The handshake was interrupted midway through. - Interrupted(MidHandshakeSslStream<S>), -} - -impl<S> From<Error> for HandshakeError<S> { - #[inline(always)] - fn from(err: Error) -> Self { - Self::Failure(err) - } -} - -/// An error or intermediate state after a TLS handshake attempt. -#[derive(Debug)] -pub enum ClientHandshakeError<S> { - /// The handshake failed. - Failure(Error), - /// The handshake was interrupted midway through. - Interrupted(MidHandshakeClientBuilder<S>), -} - -impl<S> From<Error> for ClientHandshakeError<S> { - #[inline(always)] - fn from(err: Error) -> Self { - Self::Failure(err) - } -} - -/// An SSL stream midway through the handshake process. -#[derive(Debug)] -pub struct MidHandshakeSslStream<S> { - stream: SslStream<S>, - error: Error, -} - -impl<S> MidHandshakeSslStream<S> { - /// Returns a shared reference to the inner stream. - #[inline(always)] - #[must_use] - pub fn get_ref(&self) -> &S { - self.stream.get_ref() - } - - /// Returns a mutable reference to the inner stream. - #[inline(always)] - pub fn get_mut(&mut self) -> &mut S { - self.stream.get_mut() - } - - /// Returns a shared reference to the `SslContext` of the stream. - #[inline(always)] - #[must_use] - pub fn context(&self) -> &SslContext { - self.stream.context() - } - - /// Returns a mutable reference to the `SslContext` of the stream. - #[inline(always)] - pub fn context_mut(&mut self) -> &mut SslContext { - self.stream.context_mut() - } - - /// Returns `true` iff `break_on_server_auth` was set and the handshake has - /// progressed to that point. - #[inline(always)] - #[must_use] - pub fn server_auth_completed(&self) -> bool { - self.error.code() == errSSLPeerAuthCompleted - } - - /// Returns `true` iff `break_on_cert_requested` was set and the handshake - /// has progressed to that point. - #[inline(always)] - #[must_use] - pub fn client_cert_requested(&self) -> bool { - self.error.code() == errSSLClientCertRequested - } - - /// Returns `true` iff the underlying stream returned an error with the - /// `WouldBlock` kind. - #[inline(always)] - #[must_use] - pub fn would_block(&self) -> bool { - self.error.code() == errSSLWouldBlock - } - - /// Returns the error which caused the handshake interruption. - #[inline(always)] - #[must_use] - pub fn error(&self) -> &Error { - &self.error - } - - /// Restarts the handshake process. - #[inline(always)] - pub fn handshake(self) -> result::Result<SslStream<S>, HandshakeError<S>> { - self.stream.handshake() - } -} - -/// An SSL stream midway through the handshake process. -#[derive(Debug)] -pub struct MidHandshakeClientBuilder<S> { - stream: MidHandshakeSslStream<S>, - domain: Option<String>, - certs: Vec<SecCertificate>, - trust_certs_only: bool, - danger_accept_invalid_certs: bool, -} - -impl<S> MidHandshakeClientBuilder<S> { - /// Returns a shared reference to the inner stream. - #[inline(always)] - #[must_use] - pub fn get_ref(&self) -> &S { - self.stream.get_ref() - } - - /// Returns a mutable reference to the inner stream. - #[inline(always)] - pub fn get_mut(&mut self) -> &mut S { - self.stream.get_mut() - } - - /// Returns the error which caused the handshake interruption. - #[inline(always)] - #[must_use] - pub fn error(&self) -> &Error { - self.stream.error() - } - - /// Restarts the handshake process. - pub fn handshake(self) -> result::Result<SslStream<S>, ClientHandshakeError<S>> { - let MidHandshakeClientBuilder { - stream, - domain, - certs, - trust_certs_only, - danger_accept_invalid_certs, - } = self; - - let mut result = stream.handshake(); - loop { - let stream = match result { - Ok(stream) => return Ok(stream), - Err(HandshakeError::Interrupted(stream)) => stream, - Err(HandshakeError::Failure(err)) => { - return Err(ClientHandshakeError::Failure(err)) - } - }; - - if stream.would_block() { - let ret = MidHandshakeClientBuilder { - stream, - domain, - certs, - trust_certs_only, - danger_accept_invalid_certs, - }; - return Err(ClientHandshakeError::Interrupted(ret)); - } - - if stream.server_auth_completed() { - if danger_accept_invalid_certs { - result = stream.handshake(); - continue; - } - let mut trust = match stream.context().peer_trust2()? { - Some(trust) => trust, - None => { - result = stream.handshake(); - continue; - } - }; - trust.set_anchor_certificates(&certs)?; - trust.set_trust_anchor_certificates_only(self.trust_certs_only)?; - let policy = SecPolicy::create_ssl(SslProtocolSide::SERVER, domain.as_deref()); - trust.set_policy(&policy)?; - trust.evaluate_with_error().map_err(|error| { - #[cfg(feature = "log")] - log::warn!("SecTrustEvaluateWithError: {}", error.to_string()); - Error::from_code(error.code() as _) - })?; - result = stream.handshake(); - continue; - } - - let err = Error::from_code(stream.error().code()); - return Err(ClientHandshakeError::Failure(err)); - } - } -} - -/// Specifies the state of a TLS session. -#[derive(Debug, PartialEq, Eq)] -pub struct SessionState(SSLSessionState); - -impl SessionState { - /// The session has not yet started. - pub const IDLE: Self = Self(kSSLIdle); - - /// The session is in the handshake process. - pub const HANDSHAKE: Self = Self(kSSLHandshake); - - /// The session is connected. - pub const CONNECTED: Self = Self(kSSLConnected); - - /// The session has been terminated. - pub const CLOSED: Self = Self(kSSLClosed); - - /// The session has been aborted due to an error. - pub const ABORTED: Self = Self(kSSLAborted); -} - -/// Specifies a server's requirement for client certificates. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SslAuthenticate(SSLAuthenticate); - -impl SslAuthenticate { - /// Do not request a client certificate. - pub const NEVER: Self = Self(kNeverAuthenticate); - - /// Require a client certificate. - pub const ALWAYS: Self = Self(kAlwaysAuthenticate); - - /// Request but do not require a client certificate. - pub const TRY: Self = Self(kTryAuthenticate); -} - -/// Specifies the state of client certificate processing. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SslClientCertificateState(SSLClientCertificateState); - -impl SslClientCertificateState { - /// A client certificate has not been requested or sent. - pub const NONE: Self = Self(kSSLClientCertNone); - - /// A client certificate has been requested but not recieved. - pub const REQUESTED: Self = Self(kSSLClientCertRequested); - /// A client certificate has been received and successfully validated. - pub const SENT: Self = Self(kSSLClientCertSent); - - /// A client certificate has been received but has failed to validate. - pub const REJECTED: Self = Self(kSSLClientCertRejected); -} - -/// Specifies protocol versions. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct SslProtocol(SSLProtocol); - -impl SslProtocol { - /// No protocol has been or should be negotiated or specified; use the default. - pub const UNKNOWN: Self = Self(kSSLProtocolUnknown); - - /// The SSL 3.0 protocol is preferred, though SSL 2.0 may be used if the peer does not support - /// SSL 3.0. - pub const SSL3: Self = Self(kSSLProtocol3); - - /// The TLS 1.0 protocol is preferred, though lower versions may be used - /// if the peer does not support TLS 1.0. - pub const TLS1: Self = Self(kTLSProtocol1); - - /// The TLS 1.1 protocol is preferred, though lower versions may be used - /// if the peer does not support TLS 1.1. - pub const TLS11: Self = Self(kTLSProtocol11); - - /// The TLS 1.2 protocol is preferred, though lower versions may be used - /// if the peer does not support TLS 1.2. - pub const TLS12: Self = Self(kTLSProtocol12); - - /// The TLS 1.3 protocol is preferred, though lower versions may be used - /// if the peer does not support TLS 1.3. - pub const TLS13: Self = Self(kTLSProtocol13); - - /// Only the SSL 2.0 protocol is accepted. - pub const SSL2: Self = Self(kSSLProtocol2); - - /// The `DTLSv1` protocol is preferred. - pub const DTLS1: Self = Self(kDTLSProtocol1); - - /// Only the SSL 3.0 protocol is accepted. - pub const SSL3_ONLY: Self = Self(kSSLProtocol3Only); - - /// Only the TLS 1.0 protocol is accepted. - pub const TLS1_ONLY: Self = Self(kTLSProtocol1Only); - - /// All supported TLS/SSL versions are accepted. - pub const ALL: Self = Self(kSSLProtocolAll); -} - -declare_TCFType! { - /// A Secure Transport SSL/TLS context object. - SslContext, SSLContextRef -} - -impl_TCFType!(SslContext, SSLContextRef, SSLContextGetTypeID); - -impl fmt::Debug for SslContext { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut builder = fmt.debug_struct("SslContext"); - if let Ok(state) = self.state() { - builder.field("state", &state); - } - builder.finish() - } -} - -unsafe impl Sync for SslContext {} -unsafe impl Send for SslContext {} - -impl AsInner for SslContext { - type Inner = SSLContextRef; - - #[inline(always)] - fn as_inner(&self) -> SSLContextRef { - self.0 - } -} - -macro_rules! impl_options { - ($($(#[$a:meta])* const $opt:ident: $get:ident & $set:ident,)*) => { - $( - $(#[$a])* - #[inline(always)] - pub fn $set(&mut self, value: bool) -> Result<()> { - unsafe { cvt(SSLSetSessionOption(self.0, $opt, value as Boolean)) } - } - - $(#[$a])* - #[inline] - pub fn $get(&self) -> Result<bool> { - let mut value = 0; - unsafe { cvt(SSLGetSessionOption(self.0, $opt, &mut value))?; } - Ok(value != 0) - } - )* - } -} - -impl SslContext { - /// Creates a new `SslContext` for the specified side and type of SSL - /// connection. - #[inline] - pub fn new(side: SslProtocolSide, type_: SslConnectionType) -> Result<Self> { - unsafe { - let ctx = SSLCreateContext(kCFAllocatorDefault, side.0, type_.0); - Ok(Self(ctx)) - } - } - - /// Sets the fully qualified domain name of the peer. - /// - /// This will be used on the client side of a session to validate the - /// common name field of the server's certificate. It has no effect if - /// called on a server-side `SslContext`. - /// - /// It is *highly* recommended to call this method before starting the - /// handshake process. - #[inline] - pub fn set_peer_domain_name(&mut self, peer_name: &str) -> Result<()> { - unsafe { - // SSLSetPeerDomainName doesn't need a null terminated string - cvt(SSLSetPeerDomainName( - self.0, - peer_name.as_ptr().cast(), - peer_name.len(), - )) - } - } - - /// Returns the peer domain name set by `set_peer_domain_name`. - pub fn peer_domain_name(&self) -> Result<String> { - unsafe { - let mut len = 0; - cvt(SSLGetPeerDomainNameLength(self.0, &mut len))?; - let mut buf = vec![0; len]; - cvt(SSLGetPeerDomainName( - self.0, - buf.as_mut_ptr().cast(), - &mut len, - ))?; - Ok(String::from_utf8(buf).unwrap()) - } - } - - /// Sets the certificate to be used by this side of the SSL session. - /// - /// This must be called before the handshake for server-side connections, - /// and can be used on the client-side to specify a client certificate. - /// - /// The `identity` corresponds to the leaf certificate and private - /// key, and the `certs` correspond to extra certificates in the chain. - pub fn set_certificate( - &mut self, - identity: &SecIdentity, - certs: &[SecCertificate], - ) -> Result<()> { - let mut arr = vec![identity.as_CFType()]; - arr.extend(certs.iter().map(|c| c.as_CFType())); - let certs = CFArray::from_CFTypes(&arr); - - unsafe { cvt(SSLSetCertificate(self.0, certs.as_concrete_TypeRef())) } - } - - /// Sets the peer ID of this session. - /// - /// A peer ID is an opaque sequence of bytes that will be used by Secure - /// Transport to identify the peer of an SSL session. If the peer ID of - /// this session matches that of a previously terminated session, the - /// previous session can be resumed without requiring a full handshake. - #[inline] - pub fn set_peer_id(&mut self, peer_id: &[u8]) -> Result<()> { - unsafe { cvt(SSLSetPeerID(self.0, peer_id.as_ptr().cast(), peer_id.len())) } - } - - /// Returns the peer ID of this session. - pub fn peer_id(&self) -> Result<Option<&[u8]>> { - unsafe { - let mut ptr = ptr::null(); - let mut len = 0; - cvt(SSLGetPeerID(self.0, &mut ptr, &mut len))?; - if ptr.is_null() { - Ok(None) - } else { - Ok(Some(slice::from_raw_parts(ptr.cast(), len))) - } - } - } - - /// Returns the list of ciphers that are supported by Secure Transport. - pub fn supported_ciphers(&self) -> Result<Vec<CipherSuite>> { - unsafe { - let mut num_ciphers = 0; - cvt(SSLGetNumberSupportedCiphers(self.0, &mut num_ciphers))?; - let mut ciphers = vec![0; num_ciphers]; - cvt(SSLGetSupportedCiphers( - self.0, - ciphers.as_mut_ptr(), - &mut num_ciphers, - ))?; - Ok(ciphers.iter().map(|c| CipherSuite::from_raw(*c)).collect()) - } - } - - /// Returns the list of ciphers that are eligible to be used for - /// negotiation. - pub fn enabled_ciphers(&self) -> Result<Vec<CipherSuite>> { - unsafe { - let mut num_ciphers = 0; - cvt(SSLGetNumberEnabledCiphers(self.0, &mut num_ciphers))?; - let mut ciphers = vec![0; num_ciphers]; - cvt(SSLGetEnabledCiphers( - self.0, - ciphers.as_mut_ptr(), - &mut num_ciphers, - ))?; - Ok(ciphers.iter().map(|c| CipherSuite::from_raw(*c)).collect()) - } - } - - /// Sets the list of ciphers that are eligible to be used for negotiation. - pub fn set_enabled_ciphers(&mut self, ciphers: &[CipherSuite]) -> Result<()> { - let ciphers = ciphers.iter().map(|c| c.to_raw()).collect::<Vec<_>>(); - unsafe { - cvt(SSLSetEnabledCiphers( - self.0, - ciphers.as_ptr(), - ciphers.len(), - )) - } - } - - /// Returns the cipher being used by the session. - #[inline] - pub fn negotiated_cipher(&self) -> Result<CipherSuite> { - unsafe { - let mut cipher = 0; - cvt(SSLGetNegotiatedCipher(self.0, &mut cipher))?; - Ok(CipherSuite::from_raw(cipher)) - } - } - - /// Sets the requirements for client certificates. - /// - /// Should only be called on server-side sessions. - #[inline] - pub fn set_client_side_authenticate(&mut self, auth: SslAuthenticate) -> Result<()> { - unsafe { cvt(SSLSetClientSideAuthenticate(self.0, auth.0)) } - } - - /// Returns the state of client certificate processing. - #[inline] - pub fn client_certificate_state(&self) -> Result<SslClientCertificateState> { - let mut state = 0; - - unsafe { - cvt(SSLGetClientCertificateState(self.0, &mut state))?; - } - Ok(SslClientCertificateState(state)) - } - - /// Returns the `SecTrust` object corresponding to the peer. - /// - /// This can be used in conjunction with `set_break_on_server_auth` to - /// validate certificates which do not have roots in the default set. - pub fn peer_trust2(&self) -> Result<Option<SecTrust>> { - // Calling SSLCopyPeerTrust on an idle connection does not seem to be well defined, - // so explicitly check for that - if self.state()? == SessionState::IDLE { - return Err(Error::from_code(errSecBadReq)); - } - - unsafe { - let mut trust = ptr::null_mut(); - cvt(SSLCopyPeerTrust(self.0, &mut trust))?; - if trust.is_null() { - Ok(None) - } else { - Ok(Some(SecTrust::wrap_under_create_rule(trust))) - } - } - } - - /// Returns the state of the session. - #[inline] - pub fn state(&self) -> Result<SessionState> { - unsafe { - let mut state = 0; - cvt(SSLGetSessionState(self.0, &mut state))?; - Ok(SessionState(state)) - } - } - - /// Returns the protocol version being used by the session. - #[inline] - pub fn negotiated_protocol_version(&self) -> Result<SslProtocol> { - unsafe { - let mut version = 0; - cvt(SSLGetNegotiatedProtocolVersion(self.0, &mut version))?; - Ok(SslProtocol(version)) - } - } - - /// Returns the maximum protocol version allowed by the session. - #[inline] - pub fn protocol_version_max(&self) -> Result<SslProtocol> { - unsafe { - let mut version = 0; - cvt(SSLGetProtocolVersionMax(self.0, &mut version))?; - Ok(SslProtocol(version)) - } - } - - /// Sets the maximum protocol version allowed by the session. - #[inline] - pub fn set_protocol_version_max(&mut self, max_version: SslProtocol) -> Result<()> { - unsafe { cvt(SSLSetProtocolVersionMax(self.0, max_version.0)) } - } - - /// Returns the minimum protocol version allowed by the session. - #[inline] - pub fn protocol_version_min(&self) -> Result<SslProtocol> { - unsafe { - let mut version = 0; - cvt(SSLGetProtocolVersionMin(self.0, &mut version))?; - Ok(SslProtocol(version)) - } - } - - /// Sets the minimum protocol version allowed by the session. - #[inline] - pub fn set_protocol_version_min(&mut self, min_version: SslProtocol) -> Result<()> { - unsafe { cvt(SSLSetProtocolVersionMin(self.0, min_version.0)) } - } - - /// Returns the set of protocols selected via ALPN if it succeeded. - #[cfg(feature = "alpn")] - pub fn alpn_protocols(&self) -> Result<Vec<String>> { - let mut array: CFArrayRef = ptr::null(); - unsafe { - #[cfg(feature = "OSX_10_13")] - { - cvt(SSLCopyALPNProtocols(self.0, &mut array))?; - } - - #[cfg(not(feature = "OSX_10_13"))] - { - dlsym! { fn SSLCopyALPNProtocols(SSLContextRef, *mut CFArrayRef) -> OSStatus } - if let Some(f) = SSLCopyALPNProtocols.get() { - cvt(f(self.0, &mut array))?; - } else { - return Err(Error::from_code(errSecUnimplemented)); - } - } - - if array.is_null() { - return Ok(vec![]); - } - - let array = CFArray::<CFString>::wrap_under_create_rule(array); - Ok(array.into_iter().map(|p| p.to_string()).collect()) - } - } - - /// Configures the set of protocols use for ALPN. - /// - /// This is only used for client-side connections. - #[cfg(feature = "alpn")] - pub fn set_alpn_protocols(&mut self, protocols: &[&str]) -> Result<()> { - // When CFMutableArray is added to core-foundation and IntoIterator trait - // is implemented for CFMutableArray, the code below should directly collect - // into a CFMutableArray. - let protocols = CFArray::from_CFTypes( - &protocols - .iter() - .map(|proto| CFString::new(proto)) - .collect::<Vec<_>>(), - ); - - #[cfg(feature = "OSX_10_13")] - { - unsafe { cvt(SSLSetALPNProtocols(self.0, protocols.as_concrete_TypeRef())) } - } - #[cfg(not(feature = "OSX_10_13"))] - { - dlsym! { fn SSLSetALPNProtocols(SSLContextRef, CFArrayRef) -> OSStatus } - if let Some(f) = SSLSetALPNProtocols.get() { - unsafe { cvt(f(self.0, protocols.as_concrete_TypeRef())) } - } else { - Err(Error::from_code(errSecUnimplemented)) - } - } - } - - /// Sets whether the client sends the `SessionTicket` extension in its `ClientHello`. - /// - /// On its own, this will just cause the client to send an empty `SessionTicket` extension on - /// every connection. [`SslContext::set_peer_id`] must also be used to key the session - /// ticket returned by the server. - /// - /// [`SslContext::set_peer_id`]: #method.set_peer_id - #[cfg(feature = "session-tickets")] - pub fn set_session_tickets_enabled(&mut self, enabled: bool) -> Result<()> { - #[cfg(feature = "OSX_10_13")] - { - unsafe { cvt(SSLSetSessionTicketsEnabled(self.0, enabled as Boolean)) } - } - #[cfg(not(feature = "OSX_10_13"))] - { - dlsym! { fn SSLSetSessionTicketsEnabled(SSLContextRef, Boolean) -> OSStatus } - if let Some(f) = SSLSetSessionTicketsEnabled.get() { - unsafe { cvt(f(self.0, enabled as Boolean)) } - } else { - Err(Error::from_code(errSecUnimplemented)) - } - } - } - - /// Sets whether a protocol is enabled or not. - /// - /// # Note - /// - /// On OSX this is a deprecated API in favor of `set_protocol_version_max` and - /// `set_protocol_version_min`, although if you're working with OSX 10.8 or before you may have - /// to use this API instead. - #[cfg(target_os = "macos")] - #[deprecated(note = "use `set_protocol_version_max`")] - pub fn set_protocol_version_enabled( - &mut self, - protocol: SslProtocol, - enabled: bool, - ) -> Result<()> { - unsafe { - cvt(SSLSetProtocolVersionEnabled( - self.0, - protocol.0, - Boolean::from(enabled), - )) - } - } - - /// Returns the number of bytes which can be read without triggering a - /// `read` call in the underlying stream. - #[inline] - pub fn buffered_read_size(&self) -> Result<usize> { - unsafe { - let mut size = 0; - cvt(SSLGetBufferedReadSize(self.0, &mut size))?; - Ok(size) - } - } - - impl_options! { - /// If enabled, the handshake process will pause and return instead of - /// automatically validating a server's certificate. - const kSSLSessionOptionBreakOnServerAuth: break_on_server_auth & set_break_on_server_auth, - /// If enabled, the handshake process will pause and return after - /// the server requests a certificate from the client. - const kSSLSessionOptionBreakOnCertRequested: break_on_cert_requested & set_break_on_cert_requested, - /// If enabled, the handshake process will pause and return instead of - /// automatically validating a client's certificate. - const kSSLSessionOptionBreakOnClientAuth: break_on_client_auth & set_break_on_client_auth, - /// If enabled, TLS false start will be performed if an appropriate - /// cipher suite is negotiated. - /// - const kSSLSessionOptionFalseStart: false_start & set_false_start, - /// If enabled, 1/n-1 record splitting will be enabled for TLS 1.0 - /// connections using block ciphers to mitigate the BEAST attack. - /// - const kSSLSessionOptionSendOneByteRecord: send_one_byte_record & set_send_one_byte_record, - } - - fn into_stream<S>(self, stream: S) -> Result<SslStream<S>> - where - S: Read + Write, - { - unsafe { - let ret = SSLSetIOFuncs(self.0, read_func::<S>, write_func::<S>); - if ret != errSecSuccess { - return Err(Error::from_code(ret)); - } - - let stream = Connection { - stream, - err: None, - panic: None, - }; - let stream = Box::into_raw(Box::new(stream)); - let ret = SSLSetConnection(self.0, stream.cast()); - if ret != errSecSuccess { - let _conn = Box::from_raw(stream); - return Err(Error::from_code(ret)); - } - - Ok(SslStream { - ctx: self, - _m: PhantomData, - }) - } - } - - /// Performs the SSL/TLS handshake. - pub fn handshake<S>(self, stream: S) -> result::Result<SslStream<S>, HandshakeError<S>> - where - S: Read + Write, - { - self.into_stream(stream) - .map_err(HandshakeError::Failure) - .and_then(SslStream::handshake) - } -} - -struct Connection<S> { - stream: S, - err: Option<io::Error>, - panic: Option<Box<dyn Any + Send>>, -} - -// the logic here is based off of libcurl's -#[cold] -fn translate_err(e: &io::Error) -> OSStatus { - match e.kind() { - io::ErrorKind::NotFound => errSSLClosedGraceful, - io::ErrorKind::ConnectionReset => errSSLClosedAbort, - io::ErrorKind::WouldBlock | - io::ErrorKind::NotConnected => errSSLWouldBlock, - _ => errSecIO, - } -} - -unsafe extern "C" fn read_func<S>( - connection: SSLConnectionRef, - data: *mut c_void, - data_length: *mut usize, -) -> OSStatus -where - S: Read, -{ - let conn: &mut Connection<S> = &mut *(connection as *mut _); - let data = slice::from_raw_parts_mut(data.cast::<u8>(), *data_length); - let mut start = 0; - let mut ret = errSecSuccess; - - while start < data.len() { - match panic::catch_unwind(AssertUnwindSafe(|| conn.stream.read(&mut data[start..]))) { - Ok(Ok(0)) => { - ret = errSSLClosedNoNotify; - break; - } - Ok(Ok(len)) => start += len, - Ok(Err(e)) => { - ret = translate_err(&e); - conn.err = Some(e); - break; - } - Err(e) => { - ret = errSecIO; - conn.panic = Some(e); - break; - } - } - } - - *data_length = start; - ret -} - -unsafe extern "C" fn write_func<S>( - connection: SSLConnectionRef, - data: *const c_void, - data_length: *mut usize, -) -> OSStatus -where - S: Write, -{ - let conn: &mut Connection<S> = &mut *(connection as *mut _); - let data = slice::from_raw_parts(data as *mut u8, *data_length); - let mut start = 0; - let mut ret = errSecSuccess; - - while start < data.len() { - match panic::catch_unwind(AssertUnwindSafe(|| conn.stream.write(&data[start..]))) { - Ok(Ok(0)) => { - ret = errSSLClosedNoNotify; - break; - } - Ok(Ok(len)) => start += len, - Ok(Err(e)) => { - ret = translate_err(&e); - conn.err = Some(e); - break; - } - Err(e) => { - ret = errSecIO; - conn.panic = Some(e); - break; - } - } - } - - *data_length = start; - ret -} - -/// A type implementing SSL/TLS encryption over an underlying stream. -pub struct SslStream<S> { - ctx: SslContext, - _m: PhantomData<S>, -} - -impl<S: fmt::Debug> fmt::Debug for SslStream<S> { - #[cold] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("SslStream") - .field("context", &self.ctx) - .field("stream", self.get_ref()) - .finish() - } -} - -impl<S> Drop for SslStream<S> { - fn drop(&mut self) { - unsafe { - let mut conn = ptr::null(); - let ret = SSLGetConnection(self.ctx.0, &mut conn); - assert!(ret == errSecSuccess); - let _ = Box::<Connection<S>>::from_raw(conn as *mut _); - } - } -} - -impl<S> SslStream<S> { - fn handshake(mut self) -> result::Result<Self, HandshakeError<S>> { - match unsafe { SSLHandshake(self.ctx.0) } { - errSecSuccess => Ok(self), - reason @ errSSLPeerAuthCompleted - | reason @ errSSLClientCertRequested - | reason @ errSSLWouldBlock - | reason @ errSSLClientHelloReceived => { - Err(HandshakeError::Interrupted(MidHandshakeSslStream { - stream: self, - error: Error::from_code(reason), - })) - } - err => { - self.check_panic(); - Err(HandshakeError::Failure(Error::from_code(err))) - } - } - } - - /// Returns a shared reference to the inner stream. - #[inline(always)] - #[must_use] - pub fn get_ref(&self) -> &S { - &self.connection().stream - } - - /// Returns a mutable reference to the underlying stream. - #[inline(always)] - pub fn get_mut(&mut self) -> &mut S { - &mut self.connection_mut().stream - } - - /// Returns a shared reference to the `SslContext` of the stream. - #[inline(always)] - #[must_use] - pub fn context(&self) -> &SslContext { - &self.ctx - } - - /// Returns a mutable reference to the `SslContext` of the stream. - #[inline(always)] - pub fn context_mut(&mut self) -> &mut SslContext { - &mut self.ctx - } - - /// Shuts down the connection. - pub fn close(&mut self) -> result::Result<(), io::Error> { - unsafe { - let ret = SSLClose(self.ctx.0); - if ret == errSecSuccess { - Ok(()) - } else { - Err(self.get_error(ret)) - } - } - } - - fn connection(&self) -> &Connection<S> { - unsafe { - let mut conn = ptr::null(); - let ret = SSLGetConnection(self.ctx.0, &mut conn); - assert!(ret == errSecSuccess); - - &mut *(conn as *mut Connection<S>) - } - } - - fn connection_mut(&mut self) -> &mut Connection<S> { - unsafe { - let mut conn = ptr::null(); - let ret = SSLGetConnection(self.ctx.0, &mut conn); - assert!(ret == errSecSuccess); - - &mut *(conn as *mut Connection<S>) - } - } - - #[cold] - fn check_panic(&mut self) { - let conn = self.connection_mut(); - if let Some(err) = conn.panic.take() { - panic::resume_unwind(err); - } - } - - #[cold] - fn get_error(&mut self, ret: OSStatus) -> io::Error { - self.check_panic(); - - if let Some(err) = self.connection_mut().err.take() { - err - } else { - io::Error::new(io::ErrorKind::Other, Error::from_code(ret)) - } - } -} - -impl<S: Read + Write> Read for SslStream<S> { - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - // Below we base our return value off the amount of data read, so a - // zero-length buffer might cause us to erroneously interpret this - // request as an error. Instead short-circuit that logic and return - // `Ok(0)` instead. - if buf.is_empty() { - return Ok(0); - } - - // If some data was buffered but not enough to fill `buf`, SSLRead - // will try to read a new packet. This is bad because there may be - // no more data but the socket is remaining open (e.g HTTPS with - // Connection: keep-alive). - let buffered = self.context().buffered_read_size().unwrap_or(0); - let to_read = if buffered > 0 { - cmp::min(buffered, buf.len()) - } else { - buf.len() - }; - - unsafe { - let mut nread = 0; - let ret = SSLRead(self.ctx.0, buf.as_mut_ptr().cast(), to_read, &mut nread); - // SSLRead can return an error at the same time it returns the last - // chunk of data (!) - if nread > 0 { - return Ok(nread); - } - - match ret { - errSSLClosedGraceful | errSSLClosedAbort | errSSLClosedNoNotify => Ok(0), - // this error isn't fatal - errSSLPeerAuthCompleted => self.read(buf), - _ => Err(self.get_error(ret)), - } - } - } -} - -impl<S: Read + Write> Write for SslStream<S> { - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - // Like above in read, short circuit a 0-length write - if buf.is_empty() { - return Ok(0); - } - unsafe { - let mut nwritten = 0; - let ret = SSLWrite( - self.ctx.0, - buf.as_ptr().cast(), - buf.len(), - &mut nwritten, - ); - // just to be safe, base success off of nwritten rather than ret - // for the same reason as in read - if nwritten > 0 { - Ok(nwritten) - } else { - Err(self.get_error(ret)) - } - } - } - - fn flush(&mut self) -> io::Result<()> { - self.connection_mut().stream.flush() - } -} - -/// A builder type to simplify the creation of client side `SslStream`s. -#[derive(Debug)] -pub struct ClientBuilder { - identity: Option<SecIdentity>, - certs: Vec<SecCertificate>, - chain: Vec<SecCertificate>, - protocol_min: Option<SslProtocol>, - protocol_max: Option<SslProtocol>, - trust_certs_only: bool, - use_sni: bool, - danger_accept_invalid_certs: bool, - danger_accept_invalid_hostnames: bool, - whitelisted_ciphers: Vec<CipherSuite>, - blacklisted_ciphers: Vec<CipherSuite>, - #[cfg(feature = "alpn")] - alpn: Option<Vec<String>>, - #[cfg(feature = "session-tickets")] - enable_session_tickets: bool, -} - -impl Default for ClientBuilder { - #[inline(always)] - fn default() -> Self { - Self::new() - } -} - -impl ClientBuilder { - /// Creates a new builder with default options. - #[inline] - #[must_use] - pub fn new() -> Self { - Self { - identity: None, - certs: Vec::new(), - chain: Vec::new(), - protocol_min: None, - protocol_max: None, - trust_certs_only: false, - use_sni: true, - danger_accept_invalid_certs: false, - danger_accept_invalid_hostnames: false, - whitelisted_ciphers: Vec::new(), - blacklisted_ciphers: Vec::new(), - #[cfg(feature = "alpn")] - alpn: None, - #[cfg(feature = "session-tickets")] - enable_session_tickets: false, - } - } - - /// Specifies the set of root certificates to trust when - /// verifying the server's certificate. - #[inline] - pub fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self { - self.certs = certs.to_owned(); - self - } - - /// Add the certificate the set of root certificates to trust - /// when verifying the server's certificate. - #[inline] - pub fn add_anchor_certificate(&mut self, certs: &SecCertificate) -> &mut Self { - self.certs.push(certs.to_owned()); - self - } - - /// Specifies whether to trust the built-in certificates in addition - /// to specified anchor certificates. - #[inline(always)] - pub fn trust_anchor_certificates_only(&mut self, only: bool) -> &mut Self { - self.trust_certs_only = only; - self - } - - /// Specifies whether to trust invalid certificates. - /// - /// # Warning - /// - /// You should think very carefully before using this method. If invalid - /// certificates are trusted, *any* certificate for *any* site will be - /// trusted for use. This includes expired certificates. This introduces - /// significant vulnerabilities, and should only be used as a last resort. - #[inline(always)] - pub fn danger_accept_invalid_certs(&mut self, noverify: bool) -> &mut Self { - self.danger_accept_invalid_certs = noverify; - self - } - - /// Specifies whether to use Server Name Indication (SNI). - #[inline(always)] - pub fn use_sni(&mut self, use_sni: bool) -> &mut Self { - self.use_sni = use_sni; - self - } - - /// Specifies whether to verify that the server's hostname matches its certificate. - /// - /// # Warning - /// - /// You should think very carefully before using this method. If hostnames are not verified, - /// *any* valid certificate for *any* site will be trusted for use. This introduces significant - /// vulnerabilities, and should only be used as a last resort. - #[inline(always)] - pub fn danger_accept_invalid_hostnames( - &mut self, - danger_accept_invalid_hostnames: bool, - ) -> &mut Self { - self.danger_accept_invalid_hostnames = danger_accept_invalid_hostnames; - self - } - - /// Set a whitelist of enabled ciphers. Any ciphers not whitelisted will be disabled. - pub fn whitelist_ciphers(&mut self, whitelisted_ciphers: &[CipherSuite]) -> &mut Self { - self.whitelisted_ciphers = whitelisted_ciphers.to_owned(); - self - } - - /// Set a blacklist of disabled ciphers. Blacklisted ciphers will be disabled. - pub fn blacklist_ciphers(&mut self, blacklisted_ciphers: &[CipherSuite]) -> &mut Self { - self.blacklisted_ciphers = blacklisted_ciphers.to_owned(); - self - } - - /// Use the specified identity as a SSL/TLS client certificate. - pub fn identity(&mut self, identity: &SecIdentity, chain: &[SecCertificate]) -> &mut Self { - self.identity = Some(identity.clone()); - self.chain = chain.to_owned(); - self - } - - /// Configure the minimum protocol that this client will support. - #[inline(always)] - pub fn protocol_min(&mut self, min: SslProtocol) -> &mut Self { - self.protocol_min = Some(min); - self - } - - /// Configure the minimum protocol that this client will support. - #[inline(always)] - pub fn protocol_max(&mut self, max: SslProtocol) -> &mut Self { - self.protocol_max = Some(max); - self - } - - /// Configures the set of protocols used for ALPN. - #[cfg(feature = "alpn")] - pub fn alpn_protocols(&mut self, protocols: &[&str]) -> &mut Self { - self.alpn = Some(protocols.iter().map(|s| s.to_string()).collect()); - self - } - - /// Configures the use of the RFC 5077 `SessionTicket` extension. - /// - /// Defaults to `false`. - #[cfg(feature = "session-tickets")] - #[inline(always)] - pub fn enable_session_tickets(&mut self, enable: bool) -> &mut Self { - self.enable_session_tickets = enable; - self - } - - /// Initiates a new SSL/TLS session over a stream connected to the specified domain. - /// - /// If both SNI and hostname verification are disabled, the value of `domain` will be ignored. - pub fn handshake<S>( - &self, - domain: &str, - stream: S, - ) -> result::Result<SslStream<S>, ClientHandshakeError<S>> - where - S: Read + Write, - { - // the logic for trust validation is in MidHandshakeClientBuilder::connect, so run all - // of the handshake logic through that. - let stream = MidHandshakeSslStream { - stream: self.ctx_into_stream(domain, stream)?, - error: Error::from(errSecSuccess), - }; - - let certs = self.certs.clone(); - let stream = MidHandshakeClientBuilder { - stream, - domain: if self.danger_accept_invalid_hostnames { - None - } else { - Some(domain.to_string()) - }, - certs, - trust_certs_only: self.trust_certs_only, - danger_accept_invalid_certs: self.danger_accept_invalid_certs, - }; - stream.handshake() - } - - fn ctx_into_stream<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>> - where - S: Read + Write, - { - let mut ctx = SslContext::new(SslProtocolSide::CLIENT, SslConnectionType::STREAM)?; - - if self.use_sni { - ctx.set_peer_domain_name(domain)?; - } - if let Some(ref identity) = self.identity { - ctx.set_certificate(identity, &self.chain)?; - } - #[cfg(feature = "alpn")] - { - if let Some(ref alpn) = self.alpn { - ctx.set_alpn_protocols(&alpn.iter().map(|s| &**s).collect::<Vec<_>>())?; - } - } - #[cfg(feature = "session-tickets")] - { - if self.enable_session_tickets { - // We must use the domain here to ensure that we go through certificate validation - // again rather than resuming the session if the domain changes. - ctx.set_peer_id(domain.as_bytes())?; - ctx.set_session_tickets_enabled(true)?; - } - } - ctx.set_break_on_server_auth(true)?; - self.configure_protocols(&mut ctx)?; - self.configure_ciphers(&mut ctx)?; - - ctx.into_stream(stream) - } - - fn configure_protocols(&self, ctx: &mut SslContext) -> Result<()> { - if let Some(min) = self.protocol_min { - ctx.set_protocol_version_min(min)?; - } - if let Some(max) = self.protocol_max { - ctx.set_protocol_version_max(max)?; - } - Ok(()) - } - - fn configure_ciphers(&self, ctx: &mut SslContext) -> Result<()> { - let mut ciphers = if self.whitelisted_ciphers.is_empty() { - ctx.enabled_ciphers()? - } else { - self.whitelisted_ciphers.clone() - }; - - if !self.blacklisted_ciphers.is_empty() { - ciphers.retain(|cipher| !self.blacklisted_ciphers.contains(cipher)); - } - - ctx.set_enabled_ciphers(&ciphers)?; - Ok(()) - } -} - -/// A builder type to simplify the creation of server-side `SslStream`s. -#[derive(Debug)] -pub struct ServerBuilder { - identity: SecIdentity, - certs: Vec<SecCertificate>, -} - -impl ServerBuilder { - /// Creates a new `ServerBuilder` which will use the specified identity - /// and certificate chain for handshakes. - #[must_use] - pub fn new(identity: &SecIdentity, certs: &[SecCertificate]) -> Self { - Self { - identity: identity.clone(), - certs: certs.to_owned(), - } - } - - /// Creates a new `ServerBuilder` which will use the identity - /// from the given PKCS #12 data. - /// - /// This operation fails if PKCS #12 file contains zero or more than one identity. - /// - /// This is a shortcut for the most common operation. - pub fn from_pkcs12(pkcs12_der: &[u8], passphrase: &str) -> Result<Self> { - let mut identities: Vec<(SecIdentity, Vec<SecCertificate>)> = Pkcs12ImportOptions::new() - .passphrase(passphrase) - .import(pkcs12_der)? - .into_iter() - .filter_map(|idendity| { - let certs = idendity.cert_chain.unwrap_or_default(); - idendity.identity.map(|identity| (identity, certs)) - }) - .collect(); - if identities.len() == 1 { - let (identity, certs) = identities.pop().unwrap(); - Ok(ServerBuilder::new(&identity, &certs)) - } else { - // This error code is not really helpful - Err(Error::from_code(errSecParam)) - } - } - - /// Create a SSL context for lower-level stream initialization. - pub fn new_ssl_context(&self) -> Result<SslContext> { - let mut ctx = SslContext::new(SslProtocolSide::SERVER, SslConnectionType::STREAM)?; - ctx.set_certificate(&self.identity, &self.certs)?; - Ok(ctx) - } - - /// Initiates a new SSL/TLS session over a stream. - pub fn handshake<S>(&self, stream: S) -> Result<SslStream<S>> - where - S: Read + Write, - { - match self.new_ssl_context()?.handshake(stream) { - Ok(stream) => Ok(stream), - Err(HandshakeError::Interrupted(stream)) => Err(*stream.error()), - Err(HandshakeError::Failure(err)) => Err(err), - } - } -} - -#[cfg(test)] -mod test { - use std::io::prelude::*; - use std::net::TcpStream; - - use super::*; - - #[test] - fn server_builder_from_pkcs12() { - let pkcs12_der = include_bytes!("../test/server.p12"); - ServerBuilder::from_pkcs12(pkcs12_der, "password123").unwrap(); - } - - #[test] - fn connect() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - let stream = p!(TcpStream::connect("google.com:443")); - p!(ctx.handshake(stream)); - } - - #[test] - fn connect_bad_domain() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("foobar.com")); - let stream = p!(TcpStream::connect("google.com:443")); - match ctx.handshake(stream) { - Ok(_) => panic!("expected failure"), - Err(_) => {} - } - } - - #[test] - fn load_page() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - let stream = p!(TcpStream::connect("google.com:443")); - let mut stream = p!(ctx.handshake(stream)); - p!(stream.write_all(b"GET / HTTP/1.0\r\n\r\n")); - p!(stream.flush()); - let mut buf = vec![]; - p!(stream.read_to_end(&mut buf)); - println!("{}", String::from_utf8_lossy(&buf)); - } - - #[test] - fn client_no_session_ticket_resumption() { - for _ in 0..2 { - let stream = p!(TcpStream::connect("google.com:443")); - - // Manually handshake here. - let stream = MidHandshakeSslStream { - stream: ClientBuilder::new() - .ctx_into_stream("google.com", stream) - .unwrap(), - error: Error::from(errSecSuccess), - }; - - let mut result = stream.handshake(); - - if let Err(HandshakeError::Interrupted(stream)) = result { - assert!(stream.server_auth_completed()); - result = stream.handshake(); - } else { - panic!("Unexpectedly skipped server auth"); - } - - assert!(result.is_ok()); - } - } - - #[test] - #[cfg(feature = "session-tickets")] - fn client_session_ticket_resumption() { - // The first time through this loop, we should do a full handshake. The second time, we - // should immediately finish the handshake without breaking on server auth. - for i in 0..2 { - let stream = p!(TcpStream::connect("google.com:443")); - let mut builder = ClientBuilder::new(); - builder.enable_session_tickets(true); - - // Manually handshake here. - let stream = MidHandshakeSslStream { - stream: builder.ctx_into_stream("google.com", stream).unwrap(), - error: Error::from(errSecSuccess), - }; - - let mut result = stream.handshake(); - - if let Err(HandshakeError::Interrupted(stream)) = result { - assert!(stream.server_auth_completed()); - assert_eq!( - i, 0, - "Session ticket resumption did not work, server auth was not skipped" - ); - result = stream.handshake(); - } else { - assert_eq!(i, 1, "Unexpectedly skipped server auth"); - } - - assert!(result.is_ok()); - } - } - - #[test] - #[cfg(feature = "alpn")] - fn client_alpn_accept() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - p!(ctx.set_alpn_protocols(&vec!["h2"])); - let stream = p!(TcpStream::connect("google.com:443")); - let stream = ctx.handshake(stream).unwrap(); - assert_eq!(vec!["h2"], stream.context().alpn_protocols().unwrap()); - } - - #[test] - #[cfg(feature = "alpn")] - fn client_alpn_reject() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - p!(ctx.set_alpn_protocols(&vec!["h2c"])); - let stream = p!(TcpStream::connect("google.com:443")); - let stream = ctx.handshake(stream).unwrap(); - assert!(stream.context().alpn_protocols().is_err()); - } - - #[test] - fn client_no_anchor_certs() { - let stream = p!(TcpStream::connect("google.com:443")); - assert!(ClientBuilder::new() - .trust_anchor_certificates_only(true) - .handshake("google.com", stream) - .is_err()); - } - - #[test] - fn client_bad_domain() { - let stream = p!(TcpStream::connect("google.com:443")); - assert!(ClientBuilder::new() - .handshake("foobar.com", stream) - .is_err()); - } - - #[test] - fn client_bad_domain_ignored() { - let stream = p!(TcpStream::connect("google.com:443")); - ClientBuilder::new() - .danger_accept_invalid_hostnames(true) - .handshake("foobar.com", stream) - .unwrap(); - } - - #[test] - fn connect_no_verify_ssl() { - let stream = p!(TcpStream::connect("expired.badssl.com:443")); - let mut builder = ClientBuilder::new(); - builder.danger_accept_invalid_certs(true); - builder.handshake("expired.badssl.com", stream).unwrap(); - } - - #[test] - fn load_page_client() { - let stream = p!(TcpStream::connect("google.com:443")); - let mut stream = p!(ClientBuilder::new().handshake("google.com", stream)); - p!(stream.write_all(b"GET / HTTP/1.0\r\n\r\n")); - p!(stream.flush()); - let mut buf = vec![]; - p!(stream.read_to_end(&mut buf)); - println!("{}", String::from_utf8_lossy(&buf)); - } - - #[test] - #[cfg_attr(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"), ignore)] // FIXME what's going on with ios? - fn cipher_configuration() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - let ciphers = p!(ctx.enabled_ciphers()); - let ciphers = ciphers - .iter() - .enumerate() - .filter_map(|(i, c)| if i % 2 == 0 { Some(*c) } else { None }) - .collect::<Vec<_>>(); - p!(ctx.set_enabled_ciphers(&ciphers)); - assert_eq!(ciphers, p!(ctx.enabled_ciphers())); - } - - #[test] - fn test_builder_whitelist_ciphers() { - let stream = p!(TcpStream::connect("google.com:443")); - - let ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - assert!(p!(ctx.enabled_ciphers()).len() > 1); - - let ciphers = p!(ctx.enabled_ciphers()); - let cipher = ciphers.first().unwrap(); - let stream = p!(ClientBuilder::new() - .whitelist_ciphers(&[*cipher]) - .ctx_into_stream("google.com", stream)); - - assert_eq!(1, p!(stream.context().enabled_ciphers()).len()); - } - - #[test] - #[cfg_attr(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"), ignore)] // FIXME same issue as cipher_configuration - fn test_builder_blacklist_ciphers() { - let stream = p!(TcpStream::connect("google.com:443")); - - let ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - let num = p!(ctx.enabled_ciphers()).len(); - assert!(num > 1); - - let ciphers = p!(ctx.enabled_ciphers()); - let cipher = ciphers.first().unwrap(); - let stream = p!(ClientBuilder::new() - .blacklist_ciphers(&[*cipher]) - .ctx_into_stream("google.com", stream)); - - assert_eq!(num - 1, p!(stream.context().enabled_ciphers()).len()); - } - - #[test] - fn idle_context_peer_trust() { - let ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - assert!(ctx.peer_trust2().is_err()); - } - - #[test] - fn peer_id() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::SERVER, - SslConnectionType::STREAM - )); - assert!(p!(ctx.peer_id()).is_none()); - p!(ctx.set_peer_id(b"foobar")); - assert_eq!(p!(ctx.peer_id()), Some(&b"foobar"[..])); - } - - #[test] - fn peer_domain_name() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - assert_eq!("", p!(ctx.peer_domain_name())); - p!(ctx.set_peer_domain_name("foobar.com")); - assert_eq!("foobar.com", p!(ctx.peer_domain_name())); - } - - #[test] - #[should_panic(expected = "blammo")] - fn write_panic() { - struct ExplodingStream(TcpStream); - - impl Read for ExplodingStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - self.0.read(buf) - } - } - - impl Write for ExplodingStream { - fn write(&mut self, _: &[u8]) -> io::Result<usize> { - panic!("blammo"); - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - } - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - let stream = p!(TcpStream::connect("google.com:443")); - let _ = ctx.handshake(ExplodingStream(stream)); - } - - #[test] - #[should_panic(expected = "blammo")] - fn read_panic() { - struct ExplodingStream(TcpStream); - - impl Read for ExplodingStream { - fn read(&mut self, _: &mut [u8]) -> io::Result<usize> { - panic!("blammo"); - } - } - - impl Write for ExplodingStream { - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - } - - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - let stream = p!(TcpStream::connect("google.com:443")); - let _ = ctx.handshake(ExplodingStream(stream)); - } - - #[test] - fn zero_length_buffers() { - let mut ctx = p!(SslContext::new( - SslProtocolSide::CLIENT, - SslConnectionType::STREAM - )); - p!(ctx.set_peer_domain_name("google.com")); - let stream = p!(TcpStream::connect("google.com:443")); - let mut stream = ctx.handshake(stream).unwrap(); - assert_eq!(stream.write(b"").unwrap(), 0); - assert_eq!(stream.read(&mut []).unwrap(), 0); - } -} diff --git a/vendor/security-framework/src/trust.rs b/vendor/security-framework/src/trust.rs deleted file mode 100644 index 79181d9f..00000000 --- a/vendor/security-framework/src/trust.rs +++ /dev/null @@ -1,393 +0,0 @@ -//! Trust evaluation support. - -use core_foundation::array::CFArray; -#[cfg(target_os = "macos")] -use core_foundation::array::CFArrayRef; -use core_foundation::base::TCFType; -use core_foundation::data::CFData; -use core_foundation::date::CFDate; -use core_foundation_sys::base::{Boolean, CFIndex}; - -use security_framework_sys::trust::*; -use std::ptr; - -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::cvt; -use crate::key::SecKey; -use crate::policy::SecPolicy; -use core_foundation::error::{CFError, CFErrorRef}; - -/// The result of trust evaluation. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct TrustResult(SecTrustResultType); - -impl TrustResult { - /// An invalid setting or result. - pub const INVALID: Self = Self(kSecTrustResultInvalid); - - /// You may proceed. - pub const PROCEED: Self = Self(kSecTrustResultProceed); - - /// Indicates a denial by the user, do not proceed. - pub const DENY: Self = Self(kSecTrustResultDeny); - - /// The certificate is implicitly trusted. - pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified); - - /// Indicates a trust policy failure that the user can override. - pub const RECOVERABLE_TRUST_FAILURE: Self = Self(kSecTrustResultRecoverableTrustFailure); - - /// Indicates a trust policy failure that the user cannot override. - pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure); - - /// An error not related to trust validation. - pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError); -} - -impl TrustResult { - /// Returns true if the result is "successful" - specifically `PROCEED` or `UNSPECIFIED`. - #[inline] - #[must_use] - pub fn success(self) -> bool { - matches!(self, Self::PROCEED | Self::UNSPECIFIED) - } -} - -declare_TCFType! { - /// A type representing a trust evaluation for a certificate. - SecTrust, SecTrustRef -} -impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID); - -unsafe impl Sync for SecTrust {} -unsafe impl Send for SecTrust {} - -#[cfg(target_os = "macos")] -bitflags::bitflags! { - /// The option flags used to configure the evaluation of a `SecTrust`. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct TrustOptions: SecTrustOptionFlags { - /// Allow expired certificates (except for the root certificate). - const ALLOW_EXPIRED = kSecTrustOptionAllowExpired; - /// Allow CA certificates as leaf certificates. - const LEAF_IS_CA = kSecTrustOptionLeafIsCA; - /// Allow network downloads of CA certificates. - const FETCH_ISSUER_FROM_NET = kSecTrustOptionFetchIssuerFromNet; - /// Allow expired root certificates. - const ALLOW_EXPIRED_ROOT = kSecTrustOptionAllowExpiredRoot; - /// Require a positive revocation check for each certificate. - const REQUIRE_REVOCATION_PER_CERT = kSecTrustOptionRequireRevPerCert; - /// Use TrustSettings instead of anchors. - const USE_TRUST_SETTINGS = kSecTrustOptionUseTrustSettings; - /// Treat properly self-signed certificates as anchors implicitly. - const IMPLICIT_ANCHORS = kSecTrustOptionImplicitAnchors; - } -} - -impl SecTrust { - /// Creates a `SecTrustRef` that is configured with a certificate chain, for validating - /// that chain against a collection of policies. - pub fn create_with_certificates( - certs: &[SecCertificate], - policies: &[SecPolicy], - ) -> Result<Self> { - let cert_array = CFArray::from_CFTypes(certs); - let policy_array = CFArray::from_CFTypes(policies); - let mut trust = ptr::null_mut(); - unsafe { - cvt(SecTrustCreateWithCertificates( - cert_array.as_CFTypeRef(), - policy_array.as_CFTypeRef(), - &mut trust, - ))?; - Ok(Self(trust)) - } - } - - /// Sets the date and time against which the certificates in this trust object - /// are verified. - #[inline] - pub fn set_trust_verify_date(&mut self, date: &CFDate) -> Result<()> { - unsafe { cvt(SecTrustSetVerifyDate(self.0, date.as_concrete_TypeRef())) } - } - - /// Sets additional anchor certificates used to validate trust. - pub fn set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()> { - let certs = CFArray::from_CFTypes(certs); - - unsafe { - cvt(SecTrustSetAnchorCertificates( - self.0, - certs.as_concrete_TypeRef(), - )) - } - } - - /// Retrieves the anchor (root) certificates stored by macOS - #[cfg(target_os = "macos")] - pub fn copy_anchor_certificates() -> Result<Vec<SecCertificate>> { - let mut array: CFArrayRef = ptr::null(); - - unsafe { - cvt(SecTrustCopyAnchorCertificates(&mut array))?; - } - - if array.is_null() { - return Ok(vec![]); - } - - let array = unsafe { CFArray::<SecCertificate>::wrap_under_create_rule(array) }; - Ok(array.into_iter().map(|c| c.clone()).collect()) - } - - /// If set to `true`, only the certificates specified by - /// `set_anchor_certificates` will be trusted, but not globally trusted - /// certificates. - #[inline] - pub fn set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()> { - unsafe { cvt(SecTrustSetAnchorCertificatesOnly(self.0, Boolean::from(only))) } - } - - /// Sets the policy used to evaluate trust. - #[inline] - pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> { - unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) } - } - - /// Sets option flags for customizing evaluation of a trust object. - #[cfg(target_os = "macos")] - #[inline] - pub fn set_options(&mut self, options: TrustOptions) -> Result<()> { - unsafe { cvt(SecTrustSetOptions(self.0, options.bits())) } - } - - /// Indicates whether this trust object is permitted to - /// fetch missing intermediate certificates from the network. - pub fn get_network_fetch_allowed(&mut self) -> Result<bool> { - let mut allowed = 0; - - unsafe { cvt(SecTrustGetNetworkFetchAllowed(self.0, &mut allowed))? }; - - Ok(allowed != 0) - } - - /// Specifies whether this trust object is permitted to - /// fetch missing intermediate certificates from the network. - #[inline] - pub fn set_network_fetch_allowed(&mut self, allowed: bool) -> Result<()> { - unsafe { cvt(SecTrustSetNetworkFetchAllowed(self.0, allowed as u8)) } - } - - /// Attaches Online Certificate Status Protocol (OSCP) response data - /// to this trust object. - pub fn set_trust_ocsp_response<I: Iterator<Item = impl AsRef<[u8]>>>( - &mut self, - ocsp_response: I, - ) -> Result<()> { - let response: Vec<CFData> = ocsp_response - .into_iter() - .map(|bytes| CFData::from_buffer(bytes.as_ref())) - .collect(); - - let response = CFArray::from_CFTypes(&response); - - unsafe { cvt(SecTrustSetOCSPResponse(self.0, response.as_CFTypeRef())) } - } - - /// Attaches signed certificate timestamp data to this trust object. - #[cfg(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - pub fn set_signed_certificate_timestamps<I: Iterator<Item = impl AsRef<[u8]>>>( - &mut self, - scts: I, - ) -> Result<()> { - let scts: Vec<CFData> = scts - .into_iter() - .map(|bytes| CFData::from_buffer(bytes.as_ref())) - .collect(); - - let scts = CFArray::from_CFTypes(&scts); - - unsafe { cvt(SecTrustSetSignedCertificateTimestamps(self.0, scts.as_concrete_TypeRef())) } - } - - /// Returns the public key for a leaf certificate after it has been evaluated. - #[inline] - pub fn copy_public_key(&mut self) -> Result<SecKey> { - unsafe { - Ok(SecKey::wrap_under_create_rule(SecTrustCopyPublicKey( - self.0, - ))) - } - } - - /// Evaluates trust. - #[deprecated(note = "use evaluate_with_error")] - pub fn evaluate(&self) -> Result<TrustResult> { - #[allow(deprecated)] - unsafe { - let mut result = kSecTrustResultInvalid; - cvt(SecTrustEvaluate(self.0, &mut result))?; - Ok(TrustResult(result)) - } - } - - /// Evaluates trust. Requires macOS 10.14 or iOS, otherwise it just calls `evaluate()` - pub fn evaluate_with_error(&self) -> Result<(), CFError> { - #[cfg(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))] - unsafe { - let mut error: CFErrorRef = ::std::ptr::null_mut(); - if !SecTrustEvaluateWithError(self.0, &mut error) { - assert!(!error.is_null()); - let error = CFError::wrap_under_create_rule(error); - return Err(error); - } - Ok(()) - } - #[cfg(not(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))] - #[allow(deprecated)] - { - use security_framework_sys::base::errSecNotTrusted; - use security_framework_sys::base::errSecTrustSettingDeny; - - let code = match self.evaluate() { - Ok(res) if res.success() => return Ok(()), - Ok(TrustResult::DENY) => errSecTrustSettingDeny, - Ok(_) => errSecNotTrusted, - Err(err) => err.code(), - }; - Err(cferror_from_osstatus(code)) - } - } - - /// Returns the number of certificates in an evaluated certificate chain. - /// - /// Note: evaluate must first be called on the `SecTrust`. - #[inline(always)] - #[must_use] - pub fn certificate_count(&self) -> CFIndex { - unsafe { SecTrustGetCertificateCount(self.0) } - } - - /// Returns a specific certificate from the certificate chain used to evaluate trust. - /// - /// Note: evaluate must first be called on the `SecTrust`. - #[deprecated(note = "deprecated by Apple")] - #[must_use] - pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> { - #[allow(deprecated)] - unsafe { - if self.certificate_count() <= ix { - None - } else { - let certificate = SecTrustGetCertificateAtIndex(self.0, ix); - Some(SecCertificate::wrap_under_get_rule(certificate.cast())) - } - } - } -} - -#[cfg(not(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))] -extern "C" { - fn CFErrorCreate(allocator: core_foundation_sys::base::CFAllocatorRef, domain: core_foundation_sys::string::CFStringRef, code: CFIndex, userInfo: core_foundation_sys::dictionary::CFDictionaryRef) -> CFErrorRef; -} - -#[cfg(not(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))] -fn cferror_from_osstatus(code: core_foundation_sys::base::OSStatus) -> CFError { - unsafe { - let error = CFErrorCreate(ptr::null_mut(), core_foundation_sys::error::kCFErrorDomainOSStatus, code as _, ptr::null_mut()); - assert!(!error.is_null()); - CFError::wrap_under_create_rule(error) - } -} - -#[cfg(test)] -mod test { - use crate::policy::SecPolicy; - use crate::secure_transport::SslProtocolSide; - use crate::test::certificate; - use crate::trust::SecTrust; - - #[test] - #[allow(deprecated)] - fn create_with_certificates() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - assert!(!trust.evaluate().unwrap().success()); - } - - #[test] - fn create_with_certificates_new() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - assert!(trust.evaluate_with_error().is_err()); - } - - #[test] - #[allow(deprecated)] - fn certificate_count_and_at_index() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - trust.evaluate().unwrap(); - - let count = trust.certificate_count(); - assert_eq!(count, 1); - - let cert_bytes = trust.certificate_at_index(0).unwrap().to_der(); - assert_eq!(cert_bytes, certificate().to_der()); - } - - #[test] - #[allow(deprecated)] - fn certificate_count_and_at_index_new() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - assert!(trust.evaluate_with_error().is_err()); - - let count = trust.certificate_count(); - assert_eq!(count, 1); - - let cert_bytes = trust.certificate_at_index(0).unwrap().to_der(); - assert_eq!(cert_bytes, certificate().to_der()); - } - - #[test] - #[allow(deprecated)] - fn certificate_at_index_out_of_bounds() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - - let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap(); - trust.evaluate().unwrap(); - assert!(trust.certificate_at_index(1).is_none()); - - let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - assert!(trust.evaluate_with_error().is_err()); - assert!(trust.certificate_at_index(1).is_none()); - } - - #[test] - #[allow(deprecated)] - fn set_policy() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus")); - let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - trust.set_policy(&ssl_policy).unwrap(); - assert!(!trust.evaluate().unwrap().success()); - } - - #[test] - fn set_policy_new() { - let cert = certificate(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus")); - let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap(); - let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io")); - trust.set_policy(&ssl_policy).unwrap(); - assert!(trust.evaluate_with_error().is_err()); - } -} diff --git a/vendor/security-framework/src/trust_settings.rs b/vendor/security-framework/src/trust_settings.rs deleted file mode 100644 index 8ccdedc2..00000000 --- a/vendor/security-framework/src/trust_settings.rs +++ /dev/null @@ -1,305 +0,0 @@ -//! Querying trust settings. - -use core_foundation::array::{CFArray, CFArrayRef}; -use core_foundation::base::{CFIndex, TCFType}; -use core_foundation::dictionary::CFDictionary; -use core_foundation::number::CFNumber; -use core_foundation::string::CFString; - -use core_foundation_sys::base::CFTypeRef; -use security_framework_sys::base::errSecNoTrustSettings; -use security_framework_sys::base::errSecSuccess; -use security_framework_sys::trust_settings::*; - -use std::ptr; - -use crate::base::Error; -use crate::base::Result; -use crate::certificate::SecCertificate; -use crate::cvt; - -/// Which set of trust settings to query -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[repr(u32)] -pub enum Domain { - /// Per-user trust settings - User = kSecTrustSettingsDomainUser, - /// Locally administered, system-wide trust settings - Admin = kSecTrustSettingsDomainAdmin, - /// System trust settings - System = kSecTrustSettingsDomainSystem, -} - -impl From<Domain> for SecTrustSettingsDomain { - #[inline] - fn from(domain: Domain) -> SecTrustSettingsDomain { - match domain { - Domain::User => kSecTrustSettingsDomainUser, - Domain::Admin => kSecTrustSettingsDomainAdmin, - Domain::System => kSecTrustSettingsDomainSystem, - } - } -} - -/// Trust settings for a specific certificate in a specific domain -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TrustSettingsForCertificate { - /// Not used - Invalid, - - /// This is a root certificate and is trusted, either explicitly or - /// implicitly. - TrustRoot, - - /// This is a non-root certificate but is explicitly trusted. - TrustAsRoot, - - /// Cert is explicitly distrusted. - Deny, - - /// Neither trusted nor distrusted. - Unspecified, -} - -impl TrustSettingsForCertificate { - /// Create from `kSecTrustSettingsResult*` constant - fn new(value: i64) -> Self { - if value < 0 || value > i64::from(u32::max_value()) { - return Self::Invalid; - } - match value as u32 { - kSecTrustSettingsResultTrustRoot => Self::TrustRoot, - kSecTrustSettingsResultTrustAsRoot => Self::TrustAsRoot, - kSecTrustSettingsResultDeny => Self::Deny, - kSecTrustSettingsResultUnspecified => Self::Unspecified, - _ => Self::Invalid, - } - } -} - -/// Allows access to the certificates and their trust settings in a given domain. -pub struct TrustSettings { - domain: Domain, -} - -impl TrustSettings { - /// Create a new `TrustSettings` for the given domain. - /// - /// You can call `iter()` to discover the certificates with settings in this domain. - /// - /// Then you can call `tls_trust_settings_for_certificate()` with a given certificate - /// to learn what the aggregate trust setting for that certificate within this domain. - #[inline(always)] - #[must_use] - pub fn new(domain: Domain) -> Self { - Self { domain } - } - - /// Create an iterator over the certificates with settings in this domain. - /// This produces an empty iterator if there are no such certificates. - pub fn iter(&self) -> Result<TrustSettingsIter> { - let array = unsafe { - let mut array_ptr: CFArrayRef = ptr::null_mut(); - - // SecTrustSettingsCopyCertificates returns errSecNoTrustSettings - // if no items have trust settings in the given domain. We map - // that to an empty TrustSettings iterator. - match SecTrustSettingsCopyCertificates(self.domain.into(), &mut array_ptr) { - errSecNoTrustSettings => CFArray::from_CFTypes(&[]), - errSecSuccess => CFArray::<SecCertificate>::wrap_under_create_rule(array_ptr), - err => return Err(Error::from_code(err)), - } - }; - - Ok(TrustSettingsIter { index: 0, array }) - } - - ///set trust settings to ""always trust this root certificate regardless of use.". - /// Sets the trust settings for the provided certificate to "always trust this root certificate - /// regardless of use." - /// - /// This method configures the trust settings for the specified certificate, indicating that it should - /// always be trusted as a TLS root certificate, regardless of its usage. - /// - /// If successful, the trust settings are updated for the certificate in the given domain. If the - /// certificate had no previous trust settings in the domain, new trust settings are created. If the - /// certificate had existing trust settings, they are replaced with the new settings. - /// - /// It is not possible to modify per-user trust settings when not running in a GUI - /// environment, if you try it will return error `2070: errSecInternalComponent` - #[cfg(target_os="macos")] - pub fn set_trust_settings_always(&self, cert: &SecCertificate) -> Result<()> { - let domain = self.domain; - let trust_settings: CFTypeRef = ptr::null_mut(); - cvt(unsafe { - SecTrustSettingsSetTrustSettings( - cert.as_CFTypeRef() as *mut _, - domain.into(), - trust_settings, - ) - }) - } - - /// Returns the aggregate trust setting for the given certificate. - /// - /// This tells you whether the certificate should be trusted as a TLS - /// root certificate. - /// - /// If the certificate has no trust settings in the given domain, the - /// `errSecItemNotFound` error is returned. - /// - /// If the certificate has no specific trust settings for TLS in the - /// given domain `None` is returned. - /// - /// Otherwise, the specific trust settings are aggregated and returned. - pub fn tls_trust_settings_for_certificate(&self, cert: &SecCertificate) - -> Result<Option<TrustSettingsForCertificate>> { - let trust_settings = unsafe { - let mut array_ptr: CFArrayRef = ptr::null_mut(); - let cert_ptr = cert.as_CFTypeRef() as *mut _; - cvt(SecTrustSettingsCopyTrustSettings(cert_ptr, - self.domain.into(), - &mut array_ptr))?; - CFArray::<CFDictionary>::wrap_under_create_rule(array_ptr) - }; - - for settings in trust_settings.iter() { - // Reject settings for non-SSL policies - let is_not_ssl_policy = { - let policy_name_key = CFString::from_static_string("kSecTrustSettingsPolicyName"); - let ssl_policy_name = CFString::from_static_string("sslServer"); - - let maybe_name: Option<CFString> = settings - .find(policy_name_key.as_CFTypeRef().cast()) - .map(|name| unsafe { CFString::wrap_under_get_rule((*name).cast()) }); - - matches!(maybe_name, Some(ref name) if name != &ssl_policy_name) - }; - - if is_not_ssl_policy { - continue; - } - - // Evaluate "effective trust settings" for this usage constraint. - let maybe_trust_result = { - let settings_result_key = CFString::from_static_string("kSecTrustSettingsResult"); - settings - .find(settings_result_key.as_CFTypeRef().cast()) - .map(|num| unsafe { CFNumber::wrap_under_get_rule((*num).cast()) }) - .and_then(|num| num.to_i64()) - }; - - // "Note that an empty Trust Settings array means "always trust this cert, - // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot"." - let trust_result = TrustSettingsForCertificate::new(maybe_trust_result - .unwrap_or_else(|| i64::from(kSecTrustSettingsResultTrustRoot))); - - match trust_result { - TrustSettingsForCertificate::Unspecified | - TrustSettingsForCertificate::Invalid => { continue; }, - _ => return Ok(Some(trust_result)), - } - } - - // There were no more specific settings. This might mean the certificate - // is to be trusted anyway (since, eg, it's in system store), but leave - // the caller to make this decision. - Ok(None) - } -} - -/// Iterator over certificates. -pub struct TrustSettingsIter { - array: CFArray<SecCertificate>, - index: CFIndex, -} - -impl Iterator for TrustSettingsIter { - type Item = SecCertificate; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - if self.index >= self.array.len() { - None - } else { - let cert = self.array.get(self.index).unwrap(); - self.index += 1; - Some(cert.clone()) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - let left = (self.array.len() as usize).saturating_sub(self.index as usize); - (left, Some(left)) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::test::certificate; - - fn list_for_domain(domain: Domain) { - println!("--- domain: {domain:?}"); - let ts = TrustSettings::new(domain); - let iterator = ts.iter().unwrap(); - - for (i, cert) in iterator.enumerate() { - println!("cert({i:?}) = {cert:?}"); - println!(" settings = {:?}", ts.tls_trust_settings_for_certificate(&cert)); - } - println!("---"); - } - - #[test] - fn list_for_user() { - list_for_domain(Domain::User); - } - - #[test] - fn list_for_system() { - list_for_domain(Domain::System); - } - - #[test] - fn list_for_admin() { - list_for_domain(Domain::Admin); - } - - #[test] - fn test_system_certs_are_present() { - let system = TrustSettings::new(Domain::System).iter().unwrap().count(); - - // 168 at the time of writing - assert!(system > 100); - } - - #[test] - fn test_isrg_root_exists_and_is_trusted() { - let ts = TrustSettings::new(Domain::System); - assert_eq!( - ts.iter() - .unwrap() - .find(|cert| cert.subject_summary() == "ISRG Root X1") - .and_then(|cert| ts.tls_trust_settings_for_certificate(&cert).unwrap()), - None - ); - // ^ this is a case where None means "always trust", according to Apple docs: - // - // "Note that an empty Trust Settings array means "always trust this cert, - // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot"." - } - - #[test] - fn test_unknown_cert_is_not_trusted() { - let ts = TrustSettings::new(Domain::System); - let cert = certificate(); - assert_eq!(ts.tls_trust_settings_for_certificate(&cert) - .err() - .unwrap() - .message(), - Some("The specified item could not be found in the keychain.".into())); - } -} - |
