diff options
Diffstat (limited to 'vendor/security-framework/src/authorization.rs')
| -rw-r--r-- | vendor/security-framework/src/authorization.rs | 815 |
1 files changed, 0 insertions, 815 deletions
diff --git a/vendor/security-framework/src/authorization.rs b/vendor/security-framework/src/authorization.rs deleted file mode 100644 index 762addb6..00000000 --- a/vendor/security-framework/src/authorization.rs +++ /dev/null @@ -1,815 +0,0 @@ -//! 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}; -#[cfg(all(target_os = "macos", feature = "job-bless"))] -use core_foundation::base::Boolean; -use core_foundation::base::{CFTypeRef, TCFType}; -use core_foundation::bundle::CFBundleRef; -use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; -#[cfg(all(target_os = "macos", feature = "job-bless"))] -use core_foundation::error::CFError; -#[cfg(all(target_os = "macos", feature = "job-bless"))] -use core_foundation::error::CFErrorRef; -use core_foundation::string::{CFString, CFStringRef}; -use security_framework_sys::authorization as sys; -use security_framework_sys::base::errSecConversionError; -use std::ffi::{CStr, CString}; -use std::fs::File; -use std::mem::MaybeUninit; -use std::os::raw::c_void; -use std::ptr::addr_of; -use std::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. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - 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). - #[must_use] 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] - #[must_use] 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)] - #[must_use] - 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. - #[must_use] - 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. -#[derive(Copy, Clone)] -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: Flags::default(), - }; - - Ok(auth) - } -} - -impl Authorization { - /// Creates an authorization object which has no environment or associated - /// rights. - #[inline] - #[allow(clippy::should_implement_trait)] - 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. - #[allow(clippy::unnecessary_cast)] - pub fn new( - // FIXME: this should have been by reference - rights: Option<AuthorizationItemSetStorage>, - environment: Option<AuthorizationItemSetStorage>, - flags: Flags, - ) -> Result<Self> { - let rights_ptr = rights.as_ref().map_or(std::ptr::null(), |r| { - addr_of!(r.set) as *const sys::AuthorizationItemSet - }); - - let env_ptr = environment.as_ref().map_or(std::ptr::null(), |e| { - addr_of!(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()) - } - - /// Submits the executable for the given label as a `launchd` job. - #[cfg(all(target_os = "macos", feature = "job-bless"))] - pub fn job_bless(&self, label: &str) -> Result<(), CFError> { - #[link(name = "ServiceManagement", kind = "framework")] - extern "C" { - static kSMDomainSystemLaunchd: CFStringRef; - - fn SMJobBless( - domain: CFStringRef, - executableLabel: CFStringRef, - auth: sys::AuthorizationRef, - error: *mut CFErrorRef, - ) -> Boolean; - } - - unsafe { - let mut error = std::ptr::null_mut(); - SMJobBless( - kSMDomainSystemLaunchd, - CFString::new(label).as_concrete_TypeRef(), - self.handle, - &mut error, - ); - if !error.is_null() { - return Err(CFError::wrap_under_create_rule(error)); - } - - Ok(()) - } - } - - // 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::*; - - #[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", std::env::var("USER").expect("You must set the USER environment variable"))? - .add_string("password", std::env::var("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 std::env::var_os("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 std::env::var_os("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 std::env::var_os("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(()) - } -} |
