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