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