1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! Access Control support.

use std::ptr::{self, null};

use core_foundation::string::CFString;
use core_foundation::base::{TCFType, CFOptionFlags, kCFAllocatorDefault};
use security_framework_sys::access_control::{
    SecAccessControlGetTypeID, SecAccessControlCreateWithFlags,
    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
    kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    kSecAttrAccessibleWhenUnlocked,
    kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
    kSecAttrAccessibleAfterFirstUnlock
};
use security_framework_sys::base::{SecAccessControlRef, errSecParam};
use crate::base::{Error, Result};

declare_TCFType! {
    /// A type representing sec access control settings.
    SecAccessControl, SecAccessControlRef
}
impl_TCFType!(
    SecAccessControl,
    SecAccessControlRef,
    SecAccessControlGetTypeID
);

unsafe impl Sync for SecAccessControl {}
unsafe impl Send for SecAccessControl {}

/// Specify when an item is available.
pub enum ProtectionMode {
    /// The data in the keychain can only be accessed when the device is
    /// unlocked. Only available if a passcode is set on the device.
    AccessibleWhenPasscodeSetThisDeviceOnly,
    ///The data in the keychain item can be accessed only while the device is
    /// unlocked by the user.
    AccessibleWhenUnlockedThisDeviceOnly,
    /// The data in the keychain item can be accessed only while the device is
    /// unlocked by the user.
    AccessibleWhenUnlocked,
    /// The data in the keychain item cannot be accessed after a restart until
    /// the device has been unlocked once by the user.
    AccessibleAfterFirstUnlockThisDeviceOnly,
    /// The data in the keychain item cannot be accessed after a restart until
    /// the device has been unlocked once by the user.
    AccessibleAfterFirstUnlock,
}

impl SecAccessControl {
    /// Create `AccessControl` object from flags
    pub fn create_with_flags(flags: CFOptionFlags) -> Result<Self> {
        Self::create_with_protection(None, flags)
    }

    /// Create `AccessControl` object from a protection value and flags.
    pub fn create_with_protection(protection: Option<ProtectionMode>, flags: CFOptionFlags) -> Result<Self> {
        let protection_val = protection.map(|v| {
            match v {
                ProtectionMode::AccessibleWhenPasscodeSetThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) },
                ProtectionMode::AccessibleWhenUnlockedThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) },
                ProtectionMode::AccessibleWhenUnlocked => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenUnlocked) },
                ProtectionMode::AccessibleAfterFirstUnlockThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) },
                ProtectionMode::AccessibleAfterFirstUnlock => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleAfterFirstUnlock) },
            }
        });
        unsafe {
            let access_control = SecAccessControlCreateWithFlags(
                kCFAllocatorDefault,
                protection_val.map(|v| v.as_CFTypeRef()).unwrap_or(null()),
                flags,
                ptr::null_mut(),
            );
            if access_control.is_null() {
                Err(Error::from_code(errSecParam))
            } else {
                Ok(Self::wrap_under_create_rule(access_control))
            }
        }
    }
}