1 //! Keychain support. 2 3 use core_foundation::base::{Boolean, TCFType}; 4 use security_framework_sys::base::{errSecSuccess, SecKeychainRef}; 5 use security_framework_sys::keychain::*; 6 use std::ffi::CString; 7 use std::os::raw::c_void; 8 use std::os::unix::ffi::OsStrExt; 9 use std::path::Path; 10 use std::ptr; 11 12 use crate::base::{Error, Result}; 13 use crate::cvt; 14 use crate::os::macos::access::SecAccess; 15 16 pub use security_framework_sys::keychain::SecPreferencesDomain; 17 18 declare_TCFType! { 19 /// A type representing a keychain. 20 SecKeychain, SecKeychainRef 21 } 22 impl_TCFType!(SecKeychain, SecKeychainRef, SecKeychainGetTypeID); 23 24 unsafe impl Sync for SecKeychain {} 25 unsafe impl Send for SecKeychain {} 26 27 impl SecKeychain { 28 /// Creates a `SecKeychain` object corresponding to the user's default 29 /// keychain. 30 #[inline] default() -> Result<Self>31 pub fn default() -> Result<Self> { 32 unsafe { 33 let mut keychain = ptr::null_mut(); 34 cvt(SecKeychainCopyDefault(&mut keychain))?; 35 Ok(Self::wrap_under_create_rule(keychain)) 36 } 37 } 38 39 /// Creates a `SecKeychain` object corresponding to the user's default 40 /// keychain for the given domain. default_for_domain(domain: SecPreferencesDomain) -> Result<Self>41 pub fn default_for_domain(domain: SecPreferencesDomain) -> Result<Self> { 42 unsafe { 43 let mut keychain = ptr::null_mut(); 44 cvt(SecKeychainCopyDomainDefault(domain, &mut keychain))?; 45 Ok(Self::wrap_under_create_rule(keychain)) 46 } 47 } 48 49 /// Opens a keychain from a file. open<P: AsRef<Path>>(path: P) -> Result<Self>50 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> { 51 let path_name = path.as_ref().as_os_str().as_bytes(); 52 // FIXME 53 let path_name = CString::new(path_name).unwrap(); 54 55 unsafe { 56 let mut keychain = ptr::null_mut(); 57 cvt(SecKeychainOpen(path_name.as_ptr(), &mut keychain))?; 58 Ok(Self::wrap_under_create_rule(keychain)) 59 } 60 } 61 62 /// Unlocks the keychain. 63 /// 64 /// If a password is not specified, the user will be prompted to enter it. unlock(&mut self, password: Option<&str>) -> Result<()>65 pub fn unlock(&mut self, password: Option<&str>) -> Result<()> { 66 let (len, ptr, use_password) = match password { 67 Some(password) => (password.len(), password.as_ptr() as *const _, true), 68 None => (0, ptr::null(), false), 69 }; 70 71 unsafe { 72 cvt(SecKeychainUnlock( 73 self.as_concrete_TypeRef(), 74 len as u32, 75 ptr, 76 use_password as Boolean, 77 )) 78 } 79 } 80 81 /// Sets settings of the keychain. 82 #[inline] set_settings(&mut self, settings: &KeychainSettings) -> Result<()>83 pub fn set_settings(&mut self, settings: &KeychainSettings) -> Result<()> { 84 unsafe { 85 cvt(SecKeychainSetSettings( 86 self.as_concrete_TypeRef(), 87 &settings.0, 88 )) 89 } 90 } 91 92 #[cfg(target_os = "macos")] 93 /// Disables the user interface for keychain services functions that 94 /// automatically display a user interface. disable_user_interaction() -> Result<KeychainUserInteractionLock>95 pub fn disable_user_interaction() -> Result<KeychainUserInteractionLock> { 96 let code = unsafe { SecKeychainSetUserInteractionAllowed(0u8) }; 97 98 if code != errSecSuccess { 99 Err(Error::from_code(code)) 100 } else { 101 Ok(KeychainUserInteractionLock) 102 } 103 } 104 105 #[cfg(target_os = "macos")] 106 /// Indicates whether keychain services functions that normally display a 107 /// user interaction are allowed to do so. user_interaction_allowed() -> Result<bool>108 pub fn user_interaction_allowed() -> Result<bool> { 109 let mut state: Boolean = 0; 110 let code = unsafe { SecKeychainGetUserInteractionAllowed(&mut state) }; 111 112 if code != errSecSuccess { 113 Err(Error::from_code(code)) 114 } else { 115 Ok(state != 0) 116 } 117 } 118 } 119 120 /// A builder type to create new keychains. 121 #[derive(Default)] 122 pub struct CreateOptions { 123 password: Option<String>, 124 prompt_user: bool, 125 access: Option<SecAccess>, 126 } 127 128 impl CreateOptions { 129 /// Creates a new builder with default options. 130 #[inline(always)] new() -> Self131 pub fn new() -> Self { 132 Self::default() 133 } 134 135 /// Sets the password to be used to protect the keychain. 136 #[inline] password(&mut self, password: &str) -> &mut Self137 pub fn password(&mut self, password: &str) -> &mut Self { 138 self.password = Some(password.into()); 139 self 140 } 141 142 /// If set, the user will be prompted to provide a password used to 143 /// protect the keychain. 144 #[inline(always)] prompt_user(&mut self, prompt_user: bool) -> &mut Self145 pub fn prompt_user(&mut self, prompt_user: bool) -> &mut Self { 146 self.prompt_user = prompt_user; 147 self 148 } 149 150 /// Sets the access control applied to the keychain. 151 #[inline(always)] access(&mut self, access: SecAccess) -> &mut Self152 pub fn access(&mut self, access: SecAccess) -> &mut Self { 153 self.access = Some(access); 154 self 155 } 156 157 /// Creates a new keychain at the specified location on the filesystem. create<P: AsRef<Path>>(&self, path: P) -> Result<SecKeychain>158 pub fn create<P: AsRef<Path>>(&self, path: P) -> Result<SecKeychain> { 159 unsafe { 160 let path_name = path.as_ref().as_os_str().as_bytes(); 161 // FIXME 162 let path_name = CString::new(path_name).unwrap(); 163 164 let (password, password_len) = match self.password { 165 Some(ref password) => (password.as_ptr() as *const c_void, password.len() as u32), 166 None => (ptr::null(), 0), 167 }; 168 169 let access = match self.access { 170 Some(ref access) => access.as_concrete_TypeRef(), 171 None => ptr::null_mut(), 172 }; 173 174 let mut keychain = ptr::null_mut(); 175 cvt(SecKeychainCreate( 176 path_name.as_ptr(), 177 password_len, 178 password, 179 self.prompt_user as Boolean, 180 access, 181 &mut keychain, 182 ))?; 183 184 Ok(SecKeychain::wrap_under_create_rule(keychain)) 185 } 186 } 187 } 188 189 /// Settings associated with a `SecKeychain`. 190 pub struct KeychainSettings(SecKeychainSettings); 191 192 impl KeychainSettings { 193 /// Creates a new `KeychainSettings` with default settings. 194 #[inline] new() -> Self195 pub fn new() -> Self { 196 Self(SecKeychainSettings { 197 version: SEC_KEYCHAIN_SETTINGS_VERS1, 198 lockOnSleep: 0, 199 useLockInterval: 0, 200 lockInterval: i32::max_value() as u32, 201 }) 202 } 203 204 /// If set, the keychain will automatically lock when the computer sleeps. 205 /// 206 /// Defaults to `false`. 207 #[inline(always)] set_lock_on_sleep(&mut self, lock_on_sleep: bool)208 pub fn set_lock_on_sleep(&mut self, lock_on_sleep: bool) { 209 self.0.lockOnSleep = lock_on_sleep as Boolean; 210 } 211 212 /// Sets the interval of time in seconds after which the keychain is 213 /// automatically locked. 214 /// 215 /// Defaults to `None`. set_lock_interval(&mut self, lock_interval: Option<u32>)216 pub fn set_lock_interval(&mut self, lock_interval: Option<u32>) { 217 match lock_interval { 218 Some(lock_interval) => { 219 self.0.useLockInterval = 1; 220 self.0.lockInterval = lock_interval; 221 } 222 None => { 223 self.0.useLockInterval = 0; 224 self.0.lockInterval = i32::max_value() as u32; 225 } 226 } 227 } 228 } 229 230 impl Default for KeychainSettings { 231 #[inline(always)] default() -> Self232 fn default() -> Self { 233 Self::new() 234 } 235 } 236 237 #[cfg(target_os = "macos")] 238 #[must_use = "The user interaction is disabled for the lifetime of the returned object"] 239 /// Automatically re-enables user interaction. 240 pub struct KeychainUserInteractionLock; 241 242 #[cfg(target_os = "macos")] 243 impl Drop for KeychainUserInteractionLock { 244 #[inline(always)] drop(&mut self)245 fn drop(&mut self) { 246 unsafe { SecKeychainSetUserInteractionAllowed(1u8) }; 247 } 248 } 249 250 #[cfg(test)] 251 mod test { 252 use tempdir::TempDir; 253 254 use super::*; 255 256 #[test] create_options()257 fn create_options() { 258 let dir = TempDir::new("keychain").unwrap(); 259 260 let mut keychain = CreateOptions::new() 261 .password("foobar") 262 .create(dir.path().join("test.keychain")) 263 .unwrap(); 264 265 keychain.set_settings(&KeychainSettings::new()).unwrap(); 266 } 267 268 #[test] disable_user_interaction()269 fn disable_user_interaction() { 270 assert!(SecKeychain::user_interaction_allowed().unwrap()); 271 { 272 let _lock = SecKeychain::disable_user_interaction().unwrap(); 273 assert!(!SecKeychain::user_interaction_allowed().unwrap()); 274 } 275 assert!(SecKeychain::user_interaction_allowed().unwrap()); 276 } 277 } 278