security_framework/
passwords_options.rs1use crate::access_control::SecAccessControl;
6use core_foundation::base::{CFOptionFlags, CFType, TCFType};
7#[allow(unused_imports)]
8use core_foundation::boolean::CFBoolean;
9use core_foundation::dictionary::CFDictionary;
10use core_foundation::number::CFNumber;
11use core_foundation::string::{CFString, CFStringRef};
12use security_framework_sys::access_control::*;
13use security_framework_sys::item::{
14 kSecAttrAccessControl, kSecAttrAccessGroup, kSecAttrAccount, kSecAttrAuthenticationType,
15 kSecAttrComment, kSecAttrDescription, kSecAttrLabel,
16 kSecAttrPath, kSecAttrPort, kSecAttrProtocol, kSecAttrSecurityDomain, kSecAttrServer,
17 kSecAttrService, kSecClass, kSecClassGenericPassword, kSecClassInternetPassword,
18};
19use security_framework_sys::item::kSecAttrSynchronizable;
20use security_framework_sys::item::kSecAttrSynchronizableAny;
21#[cfg(any(feature = "OSX_10_15", not(target_os = "macos")))]
22use security_framework_sys::item::kSecUseDataProtectionKeychain;
23use security_framework_sys::keychain::{SecAuthenticationType, SecProtocolType};
24
25pub struct PasswordOptions {
27 #[deprecated(note = "This field should have been private. Please use setters that don't expose CFType")]
29 pub query: Vec<(CFString, CFType)>,
30}
31
32bitflags::bitflags! {
33 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35 pub struct AccessControlOptions: CFOptionFlags {
36 const USER_PRESENCE = kSecAccessControlUserPresence;
38 const BIOMETRY_ANY = kSecAccessControlBiometryAny;
40 const BIOMETRY_CURRENT_SET = kSecAccessControlBiometryCurrentSet;
42 const DEVICE_PASSCODE = kSecAccessControlDevicePasscode;
44 #[cfg(feature = "OSX_10_15")]
45 const WATCH = kSecAccessControlWatch;
47 const OR = kSecAccessControlOr;
49 const AND = kSecAccessControlAnd;
51 const PRIVATE_KEY_USAGE = kSecAccessControlPrivateKeyUsage;
53 const APPLICATION_PASSWORD = kSecAccessControlApplicationPassword;
55 }
56}
57
58impl PasswordOptions {
59 #[must_use]
63 pub fn new_generic_password(service: &str, account: &str) -> Self {
64 #[allow(deprecated)]
65 Self { query: vec![
66 (
67 unsafe { CFString::wrap_under_get_rule(kSecClass) },
68 unsafe { CFString::wrap_under_get_rule(kSecClassGenericPassword).into_CFType() },
69 ),
70 (unsafe { CFString::wrap_under_get_rule(kSecAttrService) }, CFString::from(service).into_CFType()),
71 (unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, CFString::from(account).into_CFType()),
72 ] }
73 }
74
75 #[must_use]
79 pub fn new_internet_password(
80 server: &str,
81 security_domain: Option<&str>,
82 account: &str,
83 path: &str,
84 port: Option<u16>,
85 protocol: SecProtocolType,
86 authentication_type: SecAuthenticationType,
87 ) -> Self {
88 #[allow(deprecated)]
89 let mut this = Self { query: vec![
90 (
91 unsafe { CFString::wrap_under_get_rule(kSecClass) },
92 unsafe { CFString::wrap_under_get_rule(kSecClassInternetPassword) }.into_CFType(),
93 ),
94 (unsafe { CFString::wrap_under_get_rule(kSecAttrServer) }, CFString::from(server).into_CFType()),
95 (unsafe { CFString::wrap_under_get_rule(kSecAttrPath) }, CFString::from(path).into_CFType()),
96 (unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, CFString::from(account).into_CFType()),
97 (unsafe { CFString::wrap_under_get_rule(kSecAttrProtocol) }, CFNumber::from(protocol as i32).into_CFType()),
98 (
99 unsafe { CFString::wrap_under_get_rule(kSecAttrAuthenticationType) },
100 CFNumber::from(authentication_type as i32).into_CFType(),
101 ),
102 ] };
103 if let Some(domain) = security_domain {
104 unsafe {
105 this.push_query(kSecAttrSecurityDomain, CFString::from(domain));
106 }
107 }
108 if let Some(port) = port {
109 unsafe {
110 this.push_query(kSecAttrPort, CFNumber::from(i32::from(port)));
111 }
112 }
113 this
114 }
115
116 pub fn set_access_control_options(&mut self, options: AccessControlOptions) {
118 unsafe {
119 self.push_query(kSecAttrAccessControl, SecAccessControl::create_with_flags(options.bits()).unwrap());
120 }
121 }
122
123 pub fn set_access_control(&mut self, access_control: SecAccessControl) {
125 unsafe {
126 self.push_query(kSecAttrAccessControl, access_control);
127 }
128 }
129
130 pub fn set_access_group(&mut self, group: &str) {
132 unsafe {
133 self.push_query(kSecAttrAccessGroup, CFString::from(group));
134 }
135 }
136
137 pub fn set_access_synchronized(&mut self, synchronized: Option<bool>) {
161 unsafe {
162 if let Some(synchronizable) = synchronized {
163 self.push_query(kSecAttrSynchronizable, CFBoolean::from(synchronizable));
164 } else {
165 let either = CFString::wrap_under_get_rule(kSecAttrSynchronizableAny);
166 self.push_query(kSecAttrSynchronizable, either);
167 }
168 }
169 }
170
171 pub fn set_comment(&mut self, comment: &str) {
173 unsafe {
174 self.push_query(kSecAttrComment, CFString::from(comment));
175 }
176 }
177
178 pub fn set_description(&mut self, description: &str) {
180 unsafe {
181 self.push_query(kSecAttrDescription, CFString::from(description));
182 }
183 }
184
185 pub fn set_label(&mut self, label: &str) {
187 unsafe {
188 self.push_query(kSecAttrLabel, CFString::from(label));
189 }
190 }
191
192 #[cfg(any(feature = "OSX_10_15", not(target_os = "macos")))]
193 pub fn use_protected_keychain(&mut self) {
195 unsafe {
196 self.push_query(kSecUseDataProtectionKeychain, CFBoolean::from(true));
197 }
198 }
199
200 pub(crate) unsafe fn push_query(&mut self, static_key_constant: CFStringRef, value: impl TCFType) {
203 #[allow(deprecated)]
204 self.query.push((
205 unsafe { CFString::wrap_under_get_rule(static_key_constant) },
206 value.into_CFType(),
207 ));
208 }
209
210 pub(crate) fn to_dictionary(&self) -> CFDictionary<CFString, CFType> {
211 #[allow(deprecated)]
212 CFDictionary::from_CFType_pairs(&self.query[..])
213 }
214}