1 //! Keychain support. 2 3 use core_foundation::base::{Boolean, TCFType}; 4 use security_framework_sys::base::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::Result; 13 use crate::cvt; 14 use crate::os::macos::access::SecAccess; 15 16 declare_TCFType! { 17 /// A type representing a keychain. 18 SecKeychain, SecKeychainRef 19 } 20 impl_TCFType!(SecKeychain, SecKeychainRef, SecKeychainGetTypeID); 21 22 unsafe impl Sync for SecKeychain {} 23 unsafe impl Send for SecKeychain {} 24 25 /// Deprecated. 26 pub trait SecKeychainExt { 27 /// Deprecated. default() -> Result<SecKeychain>28 fn default() -> Result<SecKeychain>; 29 30 /// Deprecated. open<P: AsRef<Path>>(path: P) -> Result<SecKeychain>31 fn open<P: AsRef<Path>>(path: P) -> Result<SecKeychain>; 32 33 /// Deprecated. unlock(&mut self, password: Option<&str>) -> Result<()>34 fn unlock(&mut self, password: Option<&str>) -> Result<()>; 35 } 36 37 impl SecKeychainExt for SecKeychain { default() -> Result<Self>38 fn default() -> Result<Self> { 39 Self::default() 40 } 41 open<P: AsRef<Path>>(path: P) -> Result<Self>42 fn open<P: AsRef<Path>>(path: P) -> Result<Self> { 43 Self::open(path) 44 } 45 unlock(&mut self, password: Option<&str>) -> Result<()>46 fn unlock(&mut self, password: Option<&str>) -> Result<()> { 47 Self::unlock(self, password) 48 } 49 } 50 51 impl SecKeychain { 52 /// Creates a `SecKeychain` object corresponding to the user's default 53 /// keychain. default() -> Result<Self>54 pub fn default() -> Result<Self> { 55 unsafe { 56 let mut keychain = ptr::null_mut(); 57 cvt(SecKeychainCopyDefault(&mut keychain))?; 58 Ok(Self::wrap_under_create_rule(keychain)) 59 } 60 } 61 62 /// Opens a keychain from a file. open<P: AsRef<Path>>(path: P) -> Result<Self>63 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> { 64 let path_name = path.as_ref().as_os_str().as_bytes(); 65 // FIXME 66 let path_name = CString::new(path_name).unwrap(); 67 68 unsafe { 69 let mut keychain = ptr::null_mut(); 70 cvt(SecKeychainOpen(path_name.as_ptr(), &mut keychain))?; 71 Ok(Self::wrap_under_create_rule(keychain)) 72 } 73 } 74 75 /// Unlocks the keychain. 76 /// 77 /// If a password is not specified, the user will be prompted to enter it. unlock(&mut self, password: Option<&str>) -> Result<()>78 pub fn unlock(&mut self, password: Option<&str>) -> Result<()> { 79 let (len, ptr, use_password) = match password { 80 Some(password) => (password.len(), password.as_ptr() as *const _, true), 81 None => (0, ptr::null(), false), 82 }; 83 84 unsafe { 85 cvt(SecKeychainUnlock( 86 self.as_concrete_TypeRef(), 87 len as u32, 88 ptr, 89 use_password as Boolean, 90 )) 91 } 92 } 93 94 /// Sets settings of the keychain. set_settings(&mut self, settings: &KeychainSettings) -> Result<()>95 pub fn set_settings(&mut self, settings: &KeychainSettings) -> Result<()> { 96 unsafe { 97 cvt(SecKeychainSetSettings( 98 self.as_concrete_TypeRef(), 99 &settings.0, 100 )) 101 } 102 } 103 } 104 105 /// A builder type to create new keychains. 106 #[derive(Default)] 107 pub struct CreateOptions { 108 password: Option<String>, 109 prompt_user: bool, 110 access: Option<SecAccess>, 111 } 112 113 impl CreateOptions { 114 /// Creates a new builder with default options. new() -> Self115 pub fn new() -> Self { 116 Self::default() 117 } 118 119 /// Sets the password to be used to protect the keychain. password(&mut self, password: &str) -> &mut Self120 pub fn password(&mut self, password: &str) -> &mut Self { 121 self.password = Some(password.into()); 122 self 123 } 124 125 /// If set, the user will be prompted to provide a password used to 126 /// protect the keychain. prompt_user(&mut self, prompt_user: bool) -> &mut Self127 pub fn prompt_user(&mut self, prompt_user: bool) -> &mut Self { 128 self.prompt_user = prompt_user; 129 self 130 } 131 132 /// Sets the access control applied to the keychain. access(&mut self, access: SecAccess) -> &mut Self133 pub fn access(&mut self, access: SecAccess) -> &mut Self { 134 self.access = Some(access); 135 self 136 } 137 138 /// Creates a new keychain at the specified location on the filesystem. create<P: AsRef<Path>>(&self, path: P) -> Result<SecKeychain>139 pub fn create<P: AsRef<Path>>(&self, path: P) -> Result<SecKeychain> { 140 unsafe { 141 let path_name = path.as_ref().as_os_str().as_bytes(); 142 // FIXME 143 let path_name = CString::new(path_name).unwrap(); 144 145 let (password, password_len) = match self.password { 146 Some(ref password) => (password.as_ptr() as *const c_void, password.len() as u32), 147 None => (ptr::null(), 0), 148 }; 149 150 let access = match self.access { 151 Some(ref access) => access.as_concrete_TypeRef(), 152 None => ptr::null_mut(), 153 }; 154 155 let mut keychain = ptr::null_mut(); 156 cvt(SecKeychainCreate( 157 path_name.as_ptr(), 158 password_len, 159 password, 160 self.prompt_user as Boolean, 161 access, 162 &mut keychain, 163 ))?; 164 165 Ok(SecKeychain::wrap_under_create_rule(keychain)) 166 } 167 } 168 } 169 170 /// Settings associated with a `SecKeychain`. 171 pub struct KeychainSettings(SecKeychainSettings); 172 173 impl KeychainSettings { 174 /// Creates a new `KeychainSettings` with default settings. new() -> Self175 pub fn new() -> Self { 176 Self(SecKeychainSettings { 177 version: SEC_KEYCHAIN_SETTINGS_VERS1, 178 lockOnSleep: 0, 179 useLockInterval: 0, 180 lockInterval: i32::max_value() as u32, 181 }) 182 } 183 184 /// If set, the keychain will automatically lock when the computer sleeps. 185 /// 186 /// Defaults to `false`. set_lock_on_sleep(&mut self, lock_on_sleep: bool)187 pub fn set_lock_on_sleep(&mut self, lock_on_sleep: bool) { 188 self.0.lockOnSleep = lock_on_sleep as Boolean; 189 } 190 191 /// Sets the interval of time in seconds after which the keychain is 192 /// automatically locked. 193 /// 194 /// Defaults to `None`. set_lock_interval(&mut self, lock_interval: Option<u32>)195 pub fn set_lock_interval(&mut self, lock_interval: Option<u32>) { 196 match lock_interval { 197 Some(lock_interval) => { 198 self.0.useLockInterval = 1; 199 self.0.lockInterval = lock_interval; 200 } 201 None => { 202 self.0.useLockInterval = 0; 203 self.0.lockInterval = i32::max_value() as u32; 204 } 205 } 206 } 207 } 208 209 #[cfg(test)] 210 mod test { 211 use tempdir::TempDir; 212 213 use super::*; 214 215 #[test] create_options()216 fn create_options() { 217 let dir = TempDir::new("keychain").unwrap(); 218 219 let mut keychain = CreateOptions::new() 220 .password("foobar") 221 .create(dir.path().join("test.keychain")) 222 .unwrap(); 223 224 keychain.set_settings(&KeychainSettings::new()).unwrap(); 225 } 226 } 227