RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.
This commit is contained in:
779
zeroidc/vendor/security-framework/src/authorization.rs
vendored
Normal file
779
zeroidc/vendor/security-framework/src/authorization.rs
vendored
Normal file
@@ -0,0 +1,779 @@
|
||||
//! Authorization Services support.
|
||||
|
||||
/// # Potential improvements
|
||||
///
|
||||
/// * When generic specialization stabilizes prevent copying from CString
|
||||
/// arguments.
|
||||
/// * AuthorizationCopyRightsAsync
|
||||
/// * Provide constants for well known item names
|
||||
use crate::base::{Error, Result};
|
||||
use core_foundation::base::{CFTypeRef, TCFType};
|
||||
use core_foundation::bundle::CFBundleRef;
|
||||
use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
|
||||
use core_foundation::string::{CFString, CFStringRef};
|
||||
use security_framework_sys::authorization as sys;
|
||||
use security_framework_sys::base::errSecConversionError;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_void;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
ffi::{CStr, CString},
|
||||
fs::File,
|
||||
};
|
||||
use std::{convert::TryInto, marker::PhantomData};
|
||||
use sys::AuthorizationExternalForm;
|
||||
|
||||
macro_rules! optional_str_to_cfref {
|
||||
($string:ident) => {{
|
||||
$string
|
||||
.map(CFString::new)
|
||||
.map_or(std::ptr::null(), |cfs| cfs.as_concrete_TypeRef())
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! cstring_or_err {
|
||||
($x:expr) => {{
|
||||
CString::new($x).map_err(|_| Error::from_code(errSecConversionError))
|
||||
}};
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// The flags used to specify authorization options.
|
||||
pub struct Flags: sys::AuthorizationFlags {
|
||||
/// An empty flag set that you use as a placeholder when you don't want
|
||||
/// any of the other flags.
|
||||
const DEFAULTS = sys::kAuthorizationFlagDefaults;
|
||||
|
||||
/// A flag that permits user interaction as needed.
|
||||
const INTERACTION_ALLOWED = sys::kAuthorizationFlagInteractionAllowed;
|
||||
|
||||
/// A flag that permits the Security Server to attempt to grant the
|
||||
/// rights requested.
|
||||
const EXTEND_RIGHTS = sys::kAuthorizationFlagExtendRights;
|
||||
|
||||
/// A flag that permits the Security Server to grant rights on an
|
||||
/// individual basis.
|
||||
const PARTIAL_RIGHTS = sys::kAuthorizationFlagPartialRights;
|
||||
|
||||
/// A flag that instructs the Security Server to revoke authorization.
|
||||
const DESTROY_RIGHTS = sys::kAuthorizationFlagDestroyRights;
|
||||
|
||||
/// A flag that instructs the Security Server to preauthorize the rights
|
||||
/// requested.
|
||||
const PREAUTHORIZE = sys::kAuthorizationFlagPreAuthorize;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Flags {
|
||||
#[inline(always)]
|
||||
fn default() -> Flags {
|
||||
Flags::DEFAULTS
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about an authorization right or the environment.
|
||||
#[repr(C)]
|
||||
pub struct AuthorizationItem(sys::AuthorizationItem);
|
||||
|
||||
impl AuthorizationItem {
|
||||
/// The required name of the authorization right or environment data.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn name(&self) -> &str {
|
||||
unsafe {
|
||||
CStr::from_ptr(self.0.name)
|
||||
.to_str()
|
||||
.expect("AuthorizationItem::name failed to convert &str to CStr")
|
||||
}
|
||||
}
|
||||
|
||||
/// The information pertaining to the name field. Do not rely on NULL
|
||||
/// termination of string data.
|
||||
#[inline]
|
||||
pub fn value(&self) -> Option<&[u8]> {
|
||||
if self.0.value.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let value =
|
||||
unsafe { std::slice::from_raw_parts(self.0.value as *const u8, self.0.valueLength) };
|
||||
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of authorization items returned and owned by the Security Server.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct AuthorizationItemSet<'a> {
|
||||
inner: *const sys::AuthorizationItemSet,
|
||||
phantom: PhantomData<&'a sys::AuthorizationItemSet>,
|
||||
}
|
||||
|
||||
impl<'a> Drop for AuthorizationItemSet<'a> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
sys::AuthorizationFreeItemSet(self.inner as *mut sys::AuthorizationItemSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by `AuthorizationItemSetBuilder` to store data pointed to by
|
||||
/// `sys::AuthorizationItemSet`.
|
||||
#[derive(Debug)]
|
||||
pub struct AuthorizationItemSetStorage {
|
||||
/// The layout of this is a little awkward because of the requirements of
|
||||
/// Apple's APIs. `items` contains pointers to data owned by `names` and
|
||||
/// `values`, so we must not modify them once `items` has been set up.
|
||||
names: Vec<CString>,
|
||||
values: Vec<Option<Vec<u8>>>,
|
||||
items: Vec<sys::AuthorizationItem>,
|
||||
|
||||
/// Must not be given to APIs which would attempt to modify it.
|
||||
///
|
||||
/// See `AuthorizationItemSet` for sets owned by the Security Server which
|
||||
/// are writable.
|
||||
pub set: sys::AuthorizationItemSet,
|
||||
}
|
||||
|
||||
impl Default for AuthorizationItemSetStorage {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
AuthorizationItemSetStorage {
|
||||
names: Vec::new(),
|
||||
values: Vec::new(),
|
||||
items: Vec::new(),
|
||||
set: sys::AuthorizationItemSet {
|
||||
count: 0,
|
||||
items: std::ptr::null_mut(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience `AuthorizationItemSetBuilder` builder which enabled you to use
|
||||
/// rust types. All names and values passed in will be copied.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AuthorizationItemSetBuilder {
|
||||
storage: AuthorizationItemSetStorage,
|
||||
}
|
||||
|
||||
// Stores AuthorizationItems contiguously, and their items separately
|
||||
impl AuthorizationItemSetBuilder {
|
||||
/// Creates a new `AuthorizationItemSetStore`, which simplifies creating
|
||||
/// owned vectors of `AuthorizationItem`s.
|
||||
#[inline(always)]
|
||||
pub fn new() -> AuthorizationItemSetBuilder {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Adds an AuthorizationItem with the name set to a right and an empty
|
||||
/// value.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn add_right<N: Into<Vec<u8>>>(mut self, name: N) -> Result<Self> {
|
||||
self.storage.names.push(cstring_or_err!(name)?);
|
||||
self.storage.values.push(None);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Adds an AuthorizationItem with arbitrary data.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn add_data<N, V>(mut self, name: N, value: V) -> Result<Self>
|
||||
where
|
||||
N: Into<Vec<u8>>,
|
||||
V: Into<Vec<u8>>,
|
||||
{
|
||||
self.storage.names.push(cstring_or_err!(name)?);
|
||||
self.storage.values.push(Some(value.into()));
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Adds an AuthorizationItem with NULL terminated string data.
|
||||
///
|
||||
/// If `name` or `value` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn add_string<N, V>(mut self, name: N, value: V) -> Result<Self>
|
||||
where
|
||||
N: Into<Vec<u8>>,
|
||||
V: Into<Vec<u8>>,
|
||||
{
|
||||
self.storage.names.push(cstring_or_err!(name)?);
|
||||
self.storage
|
||||
.values
|
||||
.push(Some(cstring_or_err!(value)?.to_bytes().to_vec()));
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Creates the `sys::AuthorizationItemSet`, and gives you ownership of the
|
||||
/// data it points to.
|
||||
pub fn build(mut self) -> AuthorizationItemSetStorage {
|
||||
self.storage.items = self
|
||||
.storage
|
||||
.names
|
||||
.iter()
|
||||
.zip(self.storage.values.iter())
|
||||
.map(|(n, v)| sys::AuthorizationItem {
|
||||
name: n.as_ptr(),
|
||||
value: v
|
||||
.as_ref()
|
||||
.map_or(std::ptr::null_mut(), |v| v.as_ptr() as *mut c_void),
|
||||
valueLength: v.as_ref().map_or(0, |v| v.len()),
|
||||
flags: 0,
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.storage.set = sys::AuthorizationItemSet {
|
||||
count: self.storage.items.len() as u32,
|
||||
items: self.storage.items.as_ptr() as *mut sys::AuthorizationItem,
|
||||
};
|
||||
|
||||
self.storage
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by `Authorization::set_item` to define the rules of he right.
|
||||
pub enum RightDefinition<'a> {
|
||||
/// The dictionary will contain the keys and values that define the rules.
|
||||
FromDictionary(&'a CFDictionary<CFStringRef, CFTypeRef>),
|
||||
|
||||
/// The specified right's rules will be duplicated.
|
||||
FromExistingRight(&'a str),
|
||||
}
|
||||
|
||||
/// A wrapper around AuthorizationCreate and functions which operate on an
|
||||
/// AuthorizationRef.
|
||||
#[derive(Debug)]
|
||||
pub struct Authorization {
|
||||
handle: sys::AuthorizationRef,
|
||||
free_flags: Flags,
|
||||
}
|
||||
|
||||
impl TryFrom<AuthorizationExternalForm> for Authorization {
|
||||
type Error = Error;
|
||||
|
||||
/// Internalizes the external representation of an authorization reference.
|
||||
#[cold]
|
||||
fn try_from(external_form: AuthorizationExternalForm) -> Result<Self> {
|
||||
let mut handle = MaybeUninit::<sys::AuthorizationRef>::uninit();
|
||||
|
||||
let status = unsafe {
|
||||
sys::AuthorizationCreateFromExternalForm(&external_form, handle.as_mut_ptr())
|
||||
};
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from_code(status));
|
||||
}
|
||||
|
||||
let auth = Authorization {
|
||||
handle: unsafe { handle.assume_init() },
|
||||
free_flags: Default::default(),
|
||||
};
|
||||
|
||||
Ok(auth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Authorization {
|
||||
/// Creates an authorization object which has no environment or associated
|
||||
/// rights.
|
||||
#[inline]
|
||||
pub fn default() -> Result<Self> {
|
||||
Self::new(None, None, Default::default())
|
||||
}
|
||||
|
||||
/// Creates an authorization reference and provides an option to authorize
|
||||
/// or preauthorize rights.
|
||||
///
|
||||
/// `rights` should be the names of the rights you want to create.
|
||||
///
|
||||
/// `environment` is used when authorizing or preauthorizing rights. Not
|
||||
/// used in OS X v10.2 and earlier. In macOS 10.3 and later, you can pass
|
||||
/// icon or prompt data to be used in the authentication dialog box. In
|
||||
/// macOS 10.4 and later, you can also pass a user name and password in
|
||||
/// order to authorize a user without user interaction.
|
||||
pub fn new(
|
||||
rights: Option<AuthorizationItemSetStorage>,
|
||||
environment: Option<AuthorizationItemSetStorage>,
|
||||
flags: Flags,
|
||||
) -> Result<Self> {
|
||||
let rights_ptr = rights.as_ref().map_or(std::ptr::null(), |r| {
|
||||
&r.set as *const sys::AuthorizationItemSet
|
||||
});
|
||||
|
||||
let env_ptr = environment.as_ref().map_or(std::ptr::null(), |e| {
|
||||
&e.set as *const sys::AuthorizationItemSet
|
||||
});
|
||||
|
||||
let mut handle = MaybeUninit::<sys::AuthorizationRef>::uninit();
|
||||
|
||||
let status = unsafe {
|
||||
sys::AuthorizationCreate(rights_ptr, env_ptr, flags.bits(), handle.as_mut_ptr())
|
||||
};
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from_code(status));
|
||||
}
|
||||
|
||||
Ok(Authorization {
|
||||
handle: unsafe { handle.assume_init() },
|
||||
free_flags: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Internalizes the external representation of an authorization reference.
|
||||
#[deprecated(since = "2.0.1", note = "Please use the TryFrom trait instead")]
|
||||
pub fn from_external_form(external_form: sys::AuthorizationExternalForm) -> Result<Self> {
|
||||
external_form.try_into()
|
||||
}
|
||||
|
||||
/// By default the rights acquired will be retained by the Security Server.
|
||||
/// Use this to ensure they are destroyed and to prevent shared rights'
|
||||
/// continued used by other processes.
|
||||
#[inline(always)]
|
||||
pub fn destroy_rights(mut self) {
|
||||
self.free_flags = Flags::DESTROY_RIGHTS;
|
||||
}
|
||||
|
||||
/// Retrieve's the right's definition as a dictionary. Use `right_exists`
|
||||
/// if you want to avoid retrieving the dictionary.
|
||||
///
|
||||
/// `name` can be a wildcard right name.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn get_right<T: Into<Vec<u8>>>(name: T) -> Result<CFDictionary<CFString, CFTypeRef>> {
|
||||
let name = cstring_or_err!(name)?;
|
||||
let mut dict = MaybeUninit::<CFDictionaryRef>::uninit();
|
||||
|
||||
let status = unsafe { sys::AuthorizationRightGet(name.as_ptr(), dict.as_mut_ptr()) };
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from_code(status));
|
||||
}
|
||||
|
||||
let dict = unsafe { CFDictionary::wrap_under_create_rule(dict.assume_init()) };
|
||||
|
||||
Ok(dict)
|
||||
}
|
||||
|
||||
/// Checks if a right exists within the policy database. This is the same as
|
||||
/// `get_right`, but avoids a dictionary allocation.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn right_exists<T: Into<Vec<u8>>>(name: T) -> Result<bool> {
|
||||
let name = cstring_or_err!(name)?;
|
||||
|
||||
let status = unsafe { sys::AuthorizationRightGet(name.as_ptr(), std::ptr::null_mut()) };
|
||||
|
||||
Ok(status == sys::errAuthorizationSuccess)
|
||||
}
|
||||
|
||||
/// Removes a right from the policy database.
|
||||
///
|
||||
/// `name` cannot be a wildcard right name.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn remove_right<T: Into<Vec<u8>>>(&self, name: T) -> Result<()> {
|
||||
let name = cstring_or_err!(name)?;
|
||||
|
||||
let status = unsafe { sys::AuthorizationRightRemove(self.handle, name.as_ptr()) };
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from_code(status));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates or updates a right entry in the policy database. Your process
|
||||
/// must have a code signature in order to be able to add rights to the
|
||||
/// authorization database.
|
||||
///
|
||||
/// `name` cannot be a wildcard right.
|
||||
///
|
||||
/// `definition` can be either a `CFDictionaryRef` containing keys defining
|
||||
/// the rules or a `CFStringRef` representing the name of another right
|
||||
/// whose rules you wish to duplicaate.
|
||||
///
|
||||
/// `description` is a key which can be used to look up localized
|
||||
/// descriptions.
|
||||
///
|
||||
/// `bundle` will be used to get localizations from if not the main bundle.
|
||||
///
|
||||
/// `localeTableName` will be used to get localizations if provided.
|
||||
///
|
||||
/// If `name` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn set_right<T: Into<Vec<u8>>>(
|
||||
&self,
|
||||
name: T,
|
||||
definition: RightDefinition<'_>,
|
||||
description: Option<&str>,
|
||||
bundle: Option<CFBundleRef>,
|
||||
locale: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let name = cstring_or_err!(name)?;
|
||||
|
||||
let definition_cfstring: CFString;
|
||||
let definition_ref = match definition {
|
||||
RightDefinition::FromDictionary(def) => def.as_CFTypeRef(),
|
||||
RightDefinition::FromExistingRight(def) => {
|
||||
definition_cfstring = CFString::new(def);
|
||||
definition_cfstring.as_CFTypeRef()
|
||||
}
|
||||
};
|
||||
|
||||
let status = unsafe {
|
||||
sys::AuthorizationRightSet(
|
||||
self.handle,
|
||||
name.as_ptr(),
|
||||
definition_ref,
|
||||
optional_str_to_cfref!(description),
|
||||
bundle.unwrap_or(std::ptr::null_mut()),
|
||||
optional_str_to_cfref!(locale),
|
||||
)
|
||||
};
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from_code(status));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An authorization plugin can store the results of an authentication
|
||||
/// operation by calling the `SetContextValue` function. You can then
|
||||
/// retrieve this supporting data, such as the user name.
|
||||
///
|
||||
/// `tag` should specify the type of data the Security Server should return.
|
||||
/// If `None`, all available information is retreieved.
|
||||
///
|
||||
/// If `tag` isn't convertable to a `CString` it will return
|
||||
/// Err(errSecConversionError).
|
||||
pub fn copy_info<T: Into<Vec<u8>>>(&self, tag: Option<T>) -> Result<AuthorizationItemSet<'_>> {
|
||||
let tag_with_nul: CString;
|
||||
|
||||
let tag_ptr = match tag {
|
||||
Some(tag) => {
|
||||
tag_with_nul = cstring_or_err!(tag)?;
|
||||
tag_with_nul.as_ptr()
|
||||
}
|
||||
None => std::ptr::null(),
|
||||
};
|
||||
|
||||
let mut inner = MaybeUninit::<*mut sys::AuthorizationItemSet>::uninit();
|
||||
|
||||
let status =
|
||||
unsafe { sys::AuthorizationCopyInfo(self.handle, tag_ptr, inner.as_mut_ptr()) };
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from(status));
|
||||
}
|
||||
|
||||
let set = AuthorizationItemSet {
|
||||
inner: unsafe { inner.assume_init() },
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
Ok(set)
|
||||
}
|
||||
|
||||
/// Creates an external representation of an authorization reference so that
|
||||
/// you can transmit it between processes.
|
||||
pub fn make_external_form(&self) -> Result<sys::AuthorizationExternalForm> {
|
||||
let mut external_form = MaybeUninit::<sys::AuthorizationExternalForm>::uninit();
|
||||
|
||||
let status =
|
||||
unsafe { sys::AuthorizationMakeExternalForm(self.handle, external_form.as_mut_ptr()) };
|
||||
|
||||
if status != sys::errAuthorizationSuccess {
|
||||
return Err(Error::from(status));
|
||||
}
|
||||
|
||||
Ok(unsafe { external_form.assume_init() })
|
||||
}
|
||||
|
||||
/// Runs an executable tool with root privileges.
|
||||
/// Discards executable's output
|
||||
#[cfg(target_os = "macos")]
|
||||
#[inline(always)]
|
||||
pub fn execute_with_privileges<P, S, I>(
|
||||
&self,
|
||||
command: P,
|
||||
arguments: I,
|
||||
flags: Flags,
|
||||
) -> Result<()>
|
||||
where
|
||||
P: AsRef<std::path::Path>,
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<std::ffi::OsStr>,
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let arguments = arguments
|
||||
.into_iter().flat_map(|a| CString::new(a.as_ref().as_bytes()))
|
||||
.collect::<Vec<_>>();
|
||||
self.execute_with_privileges_internal(command.as_ref().as_os_str().as_bytes(), &arguments, flags, false)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs an executable tool with root privileges,
|
||||
/// and returns a `File` handle to its communication pipe
|
||||
#[cfg(target_os = "macos")]
|
||||
#[inline(always)]
|
||||
pub fn execute_with_privileges_piped<P, S, I>(
|
||||
&self,
|
||||
command: P,
|
||||
arguments: I,
|
||||
flags: Flags,
|
||||
) -> Result<File>
|
||||
where
|
||||
P: AsRef<std::path::Path>,
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<std::ffi::OsStr>,
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let arguments = arguments
|
||||
.into_iter().flat_map(|a| CString::new(a.as_ref().as_bytes()))
|
||||
.collect::<Vec<_>>();
|
||||
Ok(self.execute_with_privileges_internal(command.as_ref().as_os_str().as_bytes(), &arguments, flags, true)?.unwrap())
|
||||
}
|
||||
|
||||
// Runs an executable tool with root privileges.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn execute_with_privileges_internal(
|
||||
&self,
|
||||
command: &[u8],
|
||||
arguments: &[CString],
|
||||
flags: Flags,
|
||||
make_pipe: bool,
|
||||
) -> Result<Option<File>> {
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
let c_cmd = cstring_or_err!(command)?;
|
||||
|
||||
let mut c_args = arguments.iter().map(|a| a.as_ptr() as _).collect::<Vec<_>>();
|
||||
c_args.push(std::ptr::null_mut());
|
||||
|
||||
let mut pipe: *mut libc::FILE = std::ptr::null_mut();
|
||||
|
||||
let status = unsafe {
|
||||
sys::AuthorizationExecuteWithPrivileges(
|
||||
self.handle,
|
||||
c_cmd.as_ptr(),
|
||||
flags.bits(),
|
||||
c_args.as_ptr(),
|
||||
if make_pipe { &mut pipe } else { std::ptr::null_mut() },
|
||||
)
|
||||
};
|
||||
|
||||
crate::cvt(status)?;
|
||||
Ok(if make_pipe {
|
||||
if pipe.is_null() {
|
||||
return Err(Error::from_code(32)); // EPIPE?
|
||||
}
|
||||
Some(unsafe { File::from_raw_fd(libc::fileno(pipe) as RawFd) })
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Authorization {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
sys::AuthorizationFree(self.handle, self.free_flags.bits());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core_foundation::string::CFString;
|
||||
|
||||
#[test]
|
||||
fn test_create_default_authorization() {
|
||||
Authorization::default().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_allowed_authorization() -> Result<()> {
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("system.hdd.smart")?
|
||||
.add_right("system.login.done")?
|
||||
.build();
|
||||
|
||||
Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_then_destroy_allowed_authorization() -> Result<()> {
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("system.hdd.smart")?
|
||||
.add_right("system.login.done")?
|
||||
.build();
|
||||
|
||||
let auth = Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap();
|
||||
auth.destroy_rights();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_authorization_requiring_interaction() -> Result<()> {
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("system.privilege.admin")?
|
||||
.build();
|
||||
|
||||
let error = Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap_err();
|
||||
|
||||
assert_eq!(error.code(), sys::errAuthorizationInteractionNotAllowed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_credentials_env() -> Result<AuthorizationItemSetStorage> {
|
||||
let set = AuthorizationItemSetBuilder::new()
|
||||
.add_string(
|
||||
"username",
|
||||
option_env!("USER").expect("You must set the USER environment variable"),
|
||||
)?
|
||||
.add_string(
|
||||
"password",
|
||||
option_env!("PASSWORD").expect("You must set the PASSWORD environment varible"),
|
||||
)?
|
||||
.build();
|
||||
|
||||
Ok(set)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_authorization_with_bad_credentials() -> Result<()> {
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("system.privilege.admin")?
|
||||
.build();
|
||||
|
||||
let env = AuthorizationItemSetBuilder::new()
|
||||
.add_string("username", "Tim Apple")?
|
||||
.add_string("password", "butterfly")?
|
||||
.build();
|
||||
|
||||
let error =
|
||||
Authorization::new(Some(rights), Some(env), Flags::INTERACTION_ALLOWED).unwrap_err();
|
||||
|
||||
assert_eq!(error.code(), sys::errAuthorizationDenied);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_authorization_with_credentials() -> Result<()> {
|
||||
if option_env!("PASSWORD").is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("system.privilege.admin")?
|
||||
.build();
|
||||
|
||||
let env = create_credentials_env()?;
|
||||
|
||||
Authorization::new(Some(rights), Some(env), Flags::EXTEND_RIGHTS).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_authorization_database() -> Result<()> {
|
||||
assert!(Authorization::right_exists("system.hdd.smart")?);
|
||||
assert!(!Authorization::right_exists("EMPTY")?);
|
||||
|
||||
let dict = Authorization::get_right("system.hdd.smart").unwrap();
|
||||
|
||||
let key = CFString::from_static_string("class");
|
||||
assert!(dict.contains_key(&key));
|
||||
|
||||
let invalid_key = CFString::from_static_string("EMPTY");
|
||||
assert!(!dict.contains_key(&invalid_key));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This test will only pass if its process has a valid code signature.
|
||||
#[test]
|
||||
fn test_modify_authorization_database() -> Result<()> {
|
||||
if option_env!("PASSWORD").is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("config.modify.")?
|
||||
.build();
|
||||
|
||||
let env = create_credentials_env()?;
|
||||
|
||||
let auth = Authorization::new(Some(rights), Some(env), Flags::EXTEND_RIGHTS).unwrap();
|
||||
|
||||
assert!(!Authorization::right_exists("TEST_RIGHT")?);
|
||||
|
||||
auth.set_right(
|
||||
"TEST_RIGHT",
|
||||
RightDefinition::FromExistingRight("system.hdd.smart"),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(Authorization::right_exists("TEST_RIGHT")?);
|
||||
|
||||
auth.remove_right("TEST_RIGHT").unwrap();
|
||||
|
||||
assert!(!Authorization::right_exists("TEST_RIGHT")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This test will succeed if authorization popup is approved.
|
||||
#[test]
|
||||
fn test_execute_with_privileges() -> Result<()> {
|
||||
if option_env!("PASSWORD").is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rights = AuthorizationItemSetBuilder::new()
|
||||
.add_right("system.privilege.admin")?
|
||||
.build();
|
||||
|
||||
let auth = Authorization::new(
|
||||
Some(rights),
|
||||
None,
|
||||
Flags::DEFAULTS
|
||||
| Flags::INTERACTION_ALLOWED
|
||||
| Flags::PREAUTHORIZE
|
||||
| Flags::EXTEND_RIGHTS,
|
||||
)?;
|
||||
|
||||
let file = auth.execute_with_privileges_piped("/bin/ls", &["/"], Flags::DEFAULTS)?;
|
||||
|
||||
use std::io::{self, BufRead};
|
||||
for line in io::BufReader::new(file).lines() {
|
||||
let _ = line.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user