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