blob: 8d0040160d345726991844c8b94b18173a24bd86 [file] [log] [blame]
//! Keychain support.
use core_foundation::base::{Boolean, TCFType};
use std::os::raw::c_void;
use security_framework_sys::base::SecKeychainRef;
use security_framework_sys::keychain::*;
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ptr;
use base::Result;
use cvt;
use os::macos::access::SecAccess;
declare_TCFType! {
/// A type representing a keychain.
SecKeychain, SecKeychainRef
}
impl_TCFType!(SecKeychain, SecKeychainRef, SecKeychainGetTypeID);
unsafe impl Sync for SecKeychain {}
unsafe impl Send for SecKeychain {}
/// Deprecated.
pub trait SecKeychainExt {
/// Deprecated.
fn default() -> Result<SecKeychain>;
/// Deprecated.
fn open<P: AsRef<Path>>(path: P) -> Result<SecKeychain>;
/// Deprecated.
fn unlock(&mut self, password: Option<&str>) -> Result<()>;
}
impl SecKeychainExt for SecKeychain {
fn default() -> Result<SecKeychain> {
SecKeychain::default()
}
fn open<P: AsRef<Path>>(path: P) -> Result<SecKeychain> {
SecKeychain::open(path)
}
fn unlock(&mut self, password: Option<&str>) -> Result<()> {
SecKeychain::unlock(self, password)
}
}
impl SecKeychain {
/// Creates a `SecKeychain` object corresponding to the user's default
/// keychain.
pub fn default() -> Result<SecKeychain> {
unsafe {
let mut keychain = ptr::null_mut();
cvt(SecKeychainCopyDefault(&mut keychain))?;
Ok(SecKeychain::wrap_under_create_rule(keychain))
}
}
/// Opens a keychain from a file.
pub fn open<P: AsRef<Path>>(path: P) -> Result<SecKeychain> {
let path_name = path.as_ref().as_os_str().as_bytes();
// FIXME
let path_name = CString::new(path_name).unwrap();
unsafe {
let mut keychain = ptr::null_mut();
cvt(SecKeychainOpen(path_name.as_ptr(), &mut keychain))?;
Ok(SecKeychain::wrap_under_create_rule(keychain))
}
}
/// Unlocks the keychain.
///
/// If a password is not specified, the user will be prompted to enter it.
pub fn unlock(&mut self, password: Option<&str>) -> Result<()> {
let (len, ptr, use_password) = match password {
Some(password) => (password.len(), password.as_ptr() as *const _, true),
None => (0, ptr::null(), false),
};
unsafe {
cvt(SecKeychainUnlock(
self.as_concrete_TypeRef(),
len as u32,
ptr,
use_password as Boolean,
))
}
}
/// Sets settings of the keychain.
pub fn set_settings(&mut self, settings: &KeychainSettings) -> Result<()> {
unsafe {
cvt(SecKeychainSetSettings(
self.as_concrete_TypeRef(),
&settings.0,
))
}
}
}
/// A builder type to create new keychains.
#[derive(Default)]
pub struct CreateOptions {
password: Option<String>,
prompt_user: bool,
access: Option<SecAccess>,
}
impl CreateOptions {
/// Creates a new builder with default options.
pub fn new() -> CreateOptions {
CreateOptions::default()
}
/// Sets the password to be used to protect the keychain.
pub fn password(&mut self, password: &str) -> &mut CreateOptions {
self.password = Some(password.into());
self
}
/// If set, the user will be prompted to provide a password used to
/// protect the keychain.
pub fn prompt_user(&mut self, prompt_user: bool) -> &mut CreateOptions {
self.prompt_user = prompt_user;
self
}
/// Sets the access control applied to the keychain.
pub fn access(&mut self, access: SecAccess) -> &mut CreateOptions {
self.access = Some(access);
self
}
/// Creates a new keychain at the specified location on the filesystem.
pub fn create<P: AsRef<Path>>(&self, path: P) -> Result<SecKeychain> {
unsafe {
let path_name = path.as_ref().as_os_str().as_bytes();
// FIXME
let path_name = CString::new(path_name).unwrap();
let (password, password_len) = match self.password {
Some(ref password) => (password.as_ptr() as *const c_void, password.len() as u32),
None => (ptr::null(), 0),
};
let access = match self.access {
Some(ref access) => access.as_concrete_TypeRef(),
None => ptr::null_mut(),
};
let mut keychain = ptr::null_mut();
cvt(SecKeychainCreate(
path_name.as_ptr(),
password_len,
password,
self.prompt_user as Boolean,
access,
&mut keychain,
))?;
Ok(SecKeychain::wrap_under_create_rule(keychain))
}
}
}
/// Settings associated with a `SecKeychain`.
pub struct KeychainSettings(SecKeychainSettings);
impl KeychainSettings {
/// Creates a new `KeychainSettings` with default settings.
pub fn new() -> KeychainSettings {
KeychainSettings(SecKeychainSettings {
version: SEC_KEYCHAIN_SETTINGS_VERS1,
lockOnSleep: 0,
useLockInterval: 0,
lockInterval: i32::max_value() as u32,
})
}
/// If set, the keychain will automatically lock when the computer sleeps.
///
/// Defaults to `false`.
pub fn set_lock_on_sleep(&mut self, lock_on_sleep: bool) {
self.0.lockOnSleep = lock_on_sleep as Boolean;
}
/// Sets the interval of time in seconds after which the keychain is
/// automatically locked.
///
/// Defaults to `None`.
pub fn set_lock_interval(&mut self, lock_interval: Option<u32>) {
match lock_interval {
Some(lock_interval) => {
self.0.useLockInterval = 1;
self.0.lockInterval = lock_interval;
}
None => {
self.0.useLockInterval = 0;
self.0.lockInterval = i32::max_value() as u32;
}
}
}
}
#[cfg(test)]
mod test {
use tempdir::TempDir;
use super::*;
#[test]
fn create_options() {
let dir = TempDir::new("keychain").unwrap();
let mut keychain = CreateOptions::new()
.password("foobar")
.create(dir.path().join("test.keychain"))
.unwrap();
keychain.set_settings(&KeychainSettings::new()).unwrap();
}
}