security_framework/
passwords_options.rs

1//! Support for password options, to be used with the passwords module
2//!
3
4// NB: re-export these types in the `passwords` module!
5
6use crate::access_control::SecAccessControl;
7use core_foundation::base::{CFOptionFlags, CFType, TCFType};
8use core_foundation::dictionary::CFDictionary;
9use core_foundation::number::CFNumber;
10use core_foundation::string::{CFString, CFStringRef};
11use security_framework_sys::access_control::*;
12use security_framework_sys::item::{
13    kSecAttrAccessControl, kSecAttrAccessGroup, kSecAttrAccount, kSecAttrAuthenticationType, kSecAttrPath, kSecAttrPort, kSecAttrProtocol, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrService, kSecClass, kSecClassGenericPassword, kSecClassInternetPassword
14};
15use security_framework_sys::keychain::{SecAuthenticationType, SecProtocolType};
16
17/// `PasswordOptions` constructor
18pub struct PasswordOptions {
19    /// query built for the keychain request
20    #[deprecated(note = "This field should have been private. Please use setters that don't expose CFType")]
21    pub query: Vec<(CFString, CFType)>,
22}
23
24bitflags::bitflags! {
25    /// The option flags used to configure the evaluation of a `SecAccessControl`.
26    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27    pub struct AccessControlOptions: CFOptionFlags {
28        /** Constraint to access an item with either biometry or passcode. */
29        const USER_PRESENCE = kSecAccessControlUserPresence;
30        #[cfg(feature = "OSX_10_13")]
31        /** Constraint to access an item with Touch ID for any enrolled fingers, or Face ID. */
32        const BIOMETRY_ANY = kSecAccessControlBiometryAny;
33        #[cfg(feature = "OSX_10_13")]
34        /** Constraint to access an item with Touch ID for currently enrolled fingers, or from Face ID with the currently enrolled user. */
35        const BIOMETRY_CURRENT_SET = kSecAccessControlBiometryCurrentSet;
36        /** Constraint to access an item with a passcode. */
37        const DEVICE_PASSCODE = kSecAccessControlDevicePasscode;
38        #[cfg(feature = "OSX_10_15")]
39        /** Constraint to access an item with a watch. */
40        const WATCH = kSecAccessControlWatch;
41        /** Indicates that at least one constraint must be satisfied. */
42        const OR = kSecAccessControlOr;
43        /** Indicates that all constraints must be satisfied. */
44        const AND = kSecAccessControlAnd;
45        /** Enable a private key to be used in signing a block of data or verifying a signed block. */
46        const PRIVATE_KEY_USAGE = kSecAccessControlPrivateKeyUsage;
47        /** Option to use an application-provided password for data encryption key generation. */
48        const APPLICATION_PASSWORD = kSecAccessControlApplicationPassword;
49    }
50}
51
52impl PasswordOptions {
53    /// Create a new generic password options
54    /// Generic passwords are identified by service and account.  They have other
55    /// attributes, but this interface doesn't allow specifying them.
56    #[must_use]
57    pub fn new_generic_password(service: &str, account: &str) -> Self {
58        #[allow(deprecated)]
59        Self { query: vec![
60            (
61                unsafe { CFString::wrap_under_get_rule(kSecClass) },
62                unsafe { CFString::wrap_under_get_rule(kSecClassGenericPassword).into_CFType() },
63            ),
64            (unsafe { CFString::wrap_under_get_rule(kSecAttrService) }, CFString::from(service).into_CFType()),
65            (unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, CFString::from(account).into_CFType()),
66        ] }
67    }
68
69    /// Create a new internet password options
70    /// Internet passwords are identified by a number of attributes.
71    /// They can have others, but this interface doesn't allow specifying them.
72    #[must_use]
73    pub fn new_internet_password(
74        server: &str,
75        security_domain: Option<&str>,
76        account: &str,
77        path: &str,
78        port: Option<u16>,
79        protocol: SecProtocolType,
80        authentication_type: SecAuthenticationType,
81    ) -> Self {
82        #[allow(deprecated)]
83        let mut this = Self { query: vec![
84            (
85                unsafe { CFString::wrap_under_get_rule(kSecClass) },
86                unsafe { CFString::wrap_under_get_rule(kSecClassInternetPassword) }.into_CFType(),
87            ),
88            (unsafe { CFString::wrap_under_get_rule(kSecAttrServer) }, CFString::from(server).into_CFType()),
89            (unsafe { CFString::wrap_under_get_rule(kSecAttrPath) }, CFString::from(path).into_CFType()),
90            (unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, CFString::from(account).into_CFType()),
91            (unsafe { CFString::wrap_under_get_rule(kSecAttrProtocol) }, CFNumber::from(protocol as i32).into_CFType()),
92            (
93                unsafe { CFString::wrap_under_get_rule(kSecAttrAuthenticationType) },
94                CFNumber::from(authentication_type as i32).into_CFType(),
95            ),
96        ] };
97        if let Some(domain) = security_domain {
98            unsafe {
99                this.push_query(kSecAttrSecurityDomain, CFString::from(domain));
100            }
101        }
102        if let Some(port) = port {
103            unsafe {
104                this.push_query(kSecAttrPort, CFNumber::from(i32::from(port)));
105            }
106        }
107        this
108    }
109
110    /// Add access control to the password
111    pub fn set_access_control_options(&mut self, options: AccessControlOptions) {
112        unsafe {
113            self.push_query(kSecAttrAccessControl, SecAccessControl::create_with_flags(options.bits()).unwrap());
114        }
115    }
116
117    /// Add access control to the password
118    pub fn set_access_control(&mut self, access_control: SecAccessControl) {
119        unsafe {
120            self.push_query(kSecAttrAccessControl, access_control);
121        }
122    }
123
124    /// Add access group to the password
125    pub fn set_access_group(&mut self, group: &str) {
126        unsafe {
127            self.push_query(kSecAttrAccessGroup, CFString::from(group));
128        }
129    }
130
131    /// The key must be a `kSec*` constant.
132    /// Value is any owned ObjC object, like `CFString`.
133    pub(crate) unsafe fn push_query(&mut self, static_key_constant: CFStringRef, value: impl TCFType) {
134        #[allow(deprecated)]
135        self.query.push((
136            unsafe { CFString::wrap_under_get_rule(static_key_constant) },
137            value.into_CFType(),
138        ));
139    }
140
141    pub(crate) fn to_dictionary(&self) -> CFDictionary<CFString, CFType> {
142        #[allow(deprecated)]
143        CFDictionary::from_CFType_pairs(&self.query[..])
144    }
145}