1 //! Authorization Services support.
2 
3 /// # Potential improvements
4 ///
5 /// * When generic specialization stabilizes prevent copying from CString
6 ///   arguments.
7 /// * AuthorizationCopyRightsAsync
8 /// * Provide constants for well known item names
9 use crate::base::{Error, Result};
10 use core_foundation::base::{CFTypeRef, TCFType};
11 use core_foundation::bundle::CFBundleRef;
12 use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
13 use core_foundation::string::{CFString, CFStringRef};
14 use security_framework_sys::authorization as sys;
15 use security_framework_sys::base::errSecConversionError;
16 use std::mem::MaybeUninit;
17 use std::os::raw::c_void;
18 use std::{
19     convert::TryFrom,
20     ffi::{CStr, CString},
21     fs::File,
22 };
23 use std::{convert::TryInto, marker::PhantomData};
24 use sys::AuthorizationExternalForm;
25 
26 macro_rules! optional_str_to_cfref {
27     ($string:ident) => {{
28         $string
29             .map(CFString::new)
30             .map_or(std::ptr::null(), |cfs| cfs.as_concrete_TypeRef())
31     }};
32 }
33 
34 macro_rules! cstring_or_err {
35     ($x:expr) => {{
36         CString::new($x).map_err(|_| Error::from_code(errSecConversionError))
37     }};
38 }
39 
40 bitflags::bitflags! {
41     /// The flags used to specify authorization options.
42     pub struct Flags: sys::AuthorizationFlags {
43         /// An empty flag set that you use as a placeholder when you don't want
44         /// any of the other flags.
45         const DEFAULTS = sys::kAuthorizationFlagDefaults;
46 
47         /// A flag that permits user interaction as needed.
48         const INTERACTION_ALLOWED = sys::kAuthorizationFlagInteractionAllowed;
49 
50         /// A flag that permits the Security Server to attempt to grant the
51         /// rights requested.
52         const EXTEND_RIGHTS = sys::kAuthorizationFlagExtendRights;
53 
54         /// A flag that permits the Security Server to grant rights on an
55         /// individual basis.
56         const PARTIAL_RIGHTS = sys::kAuthorizationFlagPartialRights;
57 
58         /// A flag that instructs the Security Server to revoke authorization.
59         const DESTROY_RIGHTS = sys::kAuthorizationFlagDestroyRights;
60 
61         /// A flag that instructs the Security Server to preauthorize the rights
62         /// requested.
63         const PREAUTHORIZE = sys::kAuthorizationFlagPreAuthorize;
64     }
65 }
66 
67 impl Default for Flags {
68     #[inline(always)]
default() -> Flags69     fn default() -> Flags {
70         Flags::DEFAULTS
71     }
72 }
73 
74 /// Information about an authorization right or the environment.
75 #[repr(C)]
76 pub struct AuthorizationItem(sys::AuthorizationItem);
77 
78 impl AuthorizationItem {
79     /// The required name of the authorization right or environment data.
80     ///
81     /// If `name` isn't convertable to a `CString` it will return
82     /// Err(errSecConversionError).
name(&self) -> &str83     pub fn name(&self) -> &str {
84         unsafe {
85             CStr::from_ptr(self.0.name)
86                 .to_str()
87                 .expect("AuthorizationItem::name failed to convert &str to CStr")
88         }
89     }
90 
91     /// The information pertaining to the name field. Do not rely on NULL
92     /// termination of string data.
93     #[inline]
value(&self) -> Option<&[u8]>94     pub fn value(&self) -> Option<&[u8]> {
95         if self.0.value.is_null() {
96             return None;
97         }
98 
99         let value =
100             unsafe { std::slice::from_raw_parts(self.0.value as *const u8, self.0.valueLength) };
101 
102         Some(value)
103     }
104 }
105 
106 /// A set of authorization items returned and owned by the Security Server.
107 #[derive(Debug)]
108 #[repr(C)]
109 pub struct AuthorizationItemSet<'a> {
110     inner: *const sys::AuthorizationItemSet,
111     phantom: PhantomData<&'a sys::AuthorizationItemSet>,
112 }
113 
114 impl<'a> Drop for AuthorizationItemSet<'a> {
115     #[inline]
drop(&mut self)116     fn drop(&mut self) {
117         unsafe {
118             sys::AuthorizationFreeItemSet(self.inner as *mut sys::AuthorizationItemSet);
119         }
120     }
121 }
122 
123 /// Used by `AuthorizationItemSetBuilder` to store data pointed to by
124 /// `sys::AuthorizationItemSet`.
125 #[derive(Debug)]
126 pub struct AuthorizationItemSetStorage {
127     /// The layout of this is a little awkward because of the requirements of
128     /// Apple's APIs. `items` contains pointers to data owned by `names` and
129     /// `values`, so we must not modify them once `items` has been set up.
130     names: Vec<CString>,
131     values: Vec<Option<Vec<u8>>>,
132     items: Vec<sys::AuthorizationItem>,
133 
134     /// Must not be given to APIs which would attempt to modify it.
135     ///
136     /// See `AuthorizationItemSet` for sets owned by the Security Server which
137     /// are writable.
138     pub set: sys::AuthorizationItemSet,
139 }
140 
141 impl Default for AuthorizationItemSetStorage {
142     #[inline]
default() -> Self143     fn default() -> Self {
144         AuthorizationItemSetStorage {
145             names: Vec::new(),
146             values: Vec::new(),
147             items: Vec::new(),
148             set: sys::AuthorizationItemSet {
149                 count: 0,
150                 items: std::ptr::null_mut(),
151             },
152         }
153     }
154 }
155 
156 /// A convenience `AuthorizationItemSetBuilder` builder which enabled you to use
157 /// rust types. All names and values passed in will be copied.
158 #[derive(Debug, Default)]
159 pub struct AuthorizationItemSetBuilder {
160     storage: AuthorizationItemSetStorage,
161 }
162 
163 // Stores AuthorizationItems contiguously, and their items separately
164 impl AuthorizationItemSetBuilder {
165     /// Creates a new `AuthorizationItemSetStore`, which simplifies creating
166     /// owned vectors of `AuthorizationItem`s.
167     #[inline(always)]
new() -> AuthorizationItemSetBuilder168     pub fn new() -> AuthorizationItemSetBuilder {
169         Default::default()
170     }
171 
172     /// Adds an AuthorizationItem with the name set to a right and an empty
173     /// value.
174     ///
175     /// If `name` isn't convertable to a `CString` it will return
176     /// Err(errSecConversionError).
add_right<N: Into<Vec<u8>>>(mut self, name: N) -> Result<Self>177     pub fn add_right<N: Into<Vec<u8>>>(mut self, name: N) -> Result<Self> {
178         self.storage.names.push(cstring_or_err!(name)?);
179         self.storage.values.push(None);
180         Ok(self)
181     }
182 
183     /// Adds an AuthorizationItem with arbitrary data.
184     ///
185     /// If `name` isn't convertable to a `CString` it will return
186     /// Err(errSecConversionError).
add_data<N, V>(mut self, name: N, value: V) -> Result<Self> where N: Into<Vec<u8>>, V: Into<Vec<u8>>,187     pub fn add_data<N, V>(mut self, name: N, value: V) -> Result<Self>
188     where
189         N: Into<Vec<u8>>,
190         V: Into<Vec<u8>>,
191     {
192         self.storage.names.push(cstring_or_err!(name)?);
193         self.storage.values.push(Some(value.into()));
194         Ok(self)
195     }
196 
197     /// Adds an AuthorizationItem with NULL terminated string data.
198     ///
199     /// If `name` or `value` isn't convertable to a `CString` it will return
200     /// Err(errSecConversionError).
add_string<N, V>(mut self, name: N, value: V) -> Result<Self> where N: Into<Vec<u8>>, V: Into<Vec<u8>>,201     pub fn add_string<N, V>(mut self, name: N, value: V) -> Result<Self>
202     where
203         N: Into<Vec<u8>>,
204         V: Into<Vec<u8>>,
205     {
206         self.storage.names.push(cstring_or_err!(name)?);
207         self.storage
208             .values
209             .push(Some(cstring_or_err!(value)?.to_bytes().to_vec()));
210         Ok(self)
211     }
212 
213     /// Creates the `sys::AuthorizationItemSet`, and gives you ownership of the
214     /// data it points to.
build(mut self) -> AuthorizationItemSetStorage215     pub fn build(mut self) -> AuthorizationItemSetStorage {
216         self.storage.items = self
217             .storage
218             .names
219             .iter()
220             .zip(self.storage.values.iter())
221             .map(|(n, v)| sys::AuthorizationItem {
222                 name: n.as_ptr(),
223                 value: v
224                     .as_ref()
225                     .map_or(std::ptr::null_mut(), |v| v.as_ptr() as *mut c_void),
226                 valueLength: v.as_ref().map_or(0, |v| v.len()),
227                 flags: 0,
228             })
229             .collect();
230 
231         self.storage.set = sys::AuthorizationItemSet {
232             count: self.storage.items.len() as u32,
233             items: self.storage.items.as_ptr() as *mut sys::AuthorizationItem,
234         };
235 
236         self.storage
237     }
238 }
239 
240 /// Used by `Authorization::set_item` to define the rules of he right.
241 pub enum RightDefinition<'a> {
242     /// The dictionary will contain the keys and values that define the rules.
243     FromDictionary(&'a CFDictionary<CFStringRef, CFTypeRef>),
244 
245     /// The specified right's rules will be duplicated.
246     FromExistingRight(&'a str),
247 }
248 
249 /// A wrapper around AuthorizationCreate and functions which operate on an
250 /// AuthorizationRef.
251 #[derive(Debug)]
252 pub struct Authorization {
253     handle: sys::AuthorizationRef,
254     free_flags: Flags,
255 }
256 
257 impl TryFrom<AuthorizationExternalForm> for Authorization {
258     type Error = Error;
259 
260     /// Internalizes the external representation of an authorization reference.
261     #[cold]
try_from(external_form: AuthorizationExternalForm) -> Result<Self>262     fn try_from(external_form: AuthorizationExternalForm) -> Result<Self> {
263         let mut handle = MaybeUninit::<sys::AuthorizationRef>::uninit();
264 
265         let status = unsafe {
266             sys::AuthorizationCreateFromExternalForm(&external_form, handle.as_mut_ptr())
267         };
268 
269         if status != sys::errAuthorizationSuccess {
270             return Err(Error::from_code(status));
271         }
272 
273         let auth = Authorization {
274             handle: unsafe { handle.assume_init() },
275             free_flags: Default::default(),
276         };
277 
278         Ok(auth)
279     }
280 }
281 
282 impl<'a> Authorization {
283     /// Creates an authorization object which has no environment or associated
284     /// rights.
285     #[inline]
default() -> Result<Self>286     pub fn default() -> Result<Self> {
287         Self::new(None, None, Default::default())
288     }
289 
290     /// Creates an authorization reference and provides an option to authorize
291     /// or preauthorize rights.
292     ///
293     /// `rights` should be the names of the rights you want to create.
294     ///
295     /// `environment` is used when authorizing or preauthorizing rights. Not
296     /// used in OS X v10.2 and earlier. In macOS 10.3 and later, you can pass
297     /// icon or prompt data to be used in the authentication dialog box. In
298     /// macOS 10.4 and later, you can also pass a user name and password in
299     /// order to authorize a user without user interaction.
new( rights: Option<AuthorizationItemSetStorage>, environment: Option<AuthorizationItemSetStorage>, flags: Flags, ) -> Result<Self>300     pub fn new(
301         rights: Option<AuthorizationItemSetStorage>,
302         environment: Option<AuthorizationItemSetStorage>,
303         flags: Flags,
304     ) -> Result<Self> {
305         let rights_ptr = rights.as_ref().map_or(std::ptr::null(), |r| {
306             &r.set as *const sys::AuthorizationItemSet
307         });
308 
309         let env_ptr = environment.as_ref().map_or(std::ptr::null(), |e| {
310             &e.set as *const sys::AuthorizationItemSet
311         });
312 
313         let mut handle = MaybeUninit::<sys::AuthorizationRef>::uninit();
314 
315         let status = unsafe {
316             sys::AuthorizationCreate(rights_ptr, env_ptr, flags.bits(), handle.as_mut_ptr())
317         };
318 
319         if status != sys::errAuthorizationSuccess {
320             return Err(Error::from_code(status));
321         }
322 
323         Ok(Authorization {
324             handle: unsafe { handle.assume_init() },
325             free_flags: Default::default(),
326         })
327     }
328 
329     /// Internalizes the external representation of an authorization reference.
330     #[deprecated(since = "2.0.1", note = "Please use the TryFrom trait instead")]
from_external_form(external_form: sys::AuthorizationExternalForm) -> Result<Self>331     pub fn from_external_form(external_form: sys::AuthorizationExternalForm) -> Result<Self> {
332         external_form.try_into()
333     }
334 
335     /// By default the rights acquired will be retained by the Security Server.
336     /// Use this to ensure they are destroyed and to prevent shared rights'
337     /// continued used by other processes.
338     #[inline(always)]
destroy_rights(mut self)339     pub fn destroy_rights(mut self) {
340         self.free_flags = Flags::DESTROY_RIGHTS;
341     }
342 
343     /// Retrieve's the right's definition as a dictionary. Use `right_exists`
344     /// if you want to avoid retrieving the dictionary.
345     ///
346     /// `name` can be a wildcard right name.
347     ///
348     /// If `name` isn't convertable to a `CString` it will return
349     /// Err(errSecConversionError).
get_right<T: Into<Vec<u8>>>(name: T) -> Result<CFDictionary<CFString, CFTypeRef>>350     pub fn get_right<T: Into<Vec<u8>>>(name: T) -> Result<CFDictionary<CFString, CFTypeRef>> {
351         let name = cstring_or_err!(name)?;
352         let mut dict = MaybeUninit::<CFDictionaryRef>::uninit();
353 
354         let status = unsafe { sys::AuthorizationRightGet(name.as_ptr(), dict.as_mut_ptr()) };
355 
356         if status != sys::errAuthorizationSuccess {
357             return Err(Error::from_code(status));
358         }
359 
360         let dict = unsafe { CFDictionary::wrap_under_create_rule(dict.assume_init()) };
361 
362         Ok(dict)
363     }
364 
365     /// Checks if a right exists within the policy database. This is the same as
366     /// `get_right`, but avoids a dictionary allocation.
367     ///
368     /// If `name` isn't convertable to a `CString` it will return
369     /// Err(errSecConversionError).
right_exists<T: Into<Vec<u8>>>(name: T) -> Result<bool>370     pub fn right_exists<T: Into<Vec<u8>>>(name: T) -> Result<bool> {
371         let name = cstring_or_err!(name)?;
372 
373         let status = unsafe { sys::AuthorizationRightGet(name.as_ptr(), std::ptr::null_mut()) };
374 
375         Ok(status == sys::errAuthorizationSuccess)
376     }
377 
378     /// Removes a right from the policy database.
379     ///
380     /// `name` cannot be a wildcard right name.
381     ///
382     /// If `name` isn't convertable to a `CString` it will return
383     /// Err(errSecConversionError).
remove_right<T: Into<Vec<u8>>>(&self, name: T) -> Result<()>384     pub fn remove_right<T: Into<Vec<u8>>>(&self, name: T) -> Result<()> {
385         let name = cstring_or_err!(name)?;
386 
387         let status = unsafe { sys::AuthorizationRightRemove(self.handle, name.as_ptr()) };
388 
389         if status != sys::errAuthorizationSuccess {
390             return Err(Error::from_code(status));
391         }
392 
393         Ok(())
394     }
395 
396     /// Creates or updates a right entry in the policy database. Your process
397     /// must have a code signature in order to be able to add rights to the
398     /// authorization database.
399     ///
400     /// `name` cannot be a wildcard right.
401     ///
402     /// `definition` can be either a `CFDictionaryRef` containing keys defining
403     /// the rules or a `CFStringRef` representing the name of another right
404     /// whose rules you wish to duplicaate.
405     ///
406     /// `description` is a key which can be used to look up localized
407     /// descriptions.
408     ///
409     /// `bundle` will be used to get localizations from if not the main bundle.
410     ///
411     /// `localeTableName` will be used to get localizations if provided.
412     ///
413     /// If `name` isn't convertable to a `CString` it will return
414     /// Err(errSecConversionError).
set_right<T: Into<Vec<u8>>>( &self, name: T, definition: RightDefinition<'_>, description: Option<&str>, bundle: Option<CFBundleRef>, locale: Option<&str>, ) -> Result<()>415     pub fn set_right<T: Into<Vec<u8>>>(
416         &self,
417         name: T,
418         definition: RightDefinition<'_>,
419         description: Option<&str>,
420         bundle: Option<CFBundleRef>,
421         locale: Option<&str>,
422     ) -> Result<()> {
423         let name = cstring_or_err!(name)?;
424 
425         let definition_cfstring: CFString;
426         let definition_ref = match definition {
427             RightDefinition::FromDictionary(def) => def.as_CFTypeRef(),
428             RightDefinition::FromExistingRight(def) => {
429                 definition_cfstring = CFString::new(def);
430                 definition_cfstring.as_CFTypeRef()
431             }
432         };
433 
434         let status = unsafe {
435             sys::AuthorizationRightSet(
436                 self.handle,
437                 name.as_ptr(),
438                 definition_ref,
439                 optional_str_to_cfref!(description),
440                 bundle.unwrap_or(std::ptr::null_mut()),
441                 optional_str_to_cfref!(locale),
442             )
443         };
444 
445         if status != sys::errAuthorizationSuccess {
446             return Err(Error::from_code(status));
447         }
448 
449         Ok(())
450     }
451 
452     /// An authorization plugin can store the results of an authentication
453     /// operation by calling the `SetContextValue` function. You can then
454     /// retrieve this supporting data, such as the user name.
455     ///
456     /// `tag` should specify the type of data the Security Server should return.
457     /// If `None`, all available information is retreieved.
458     ///
459     /// If `tag` isn't convertable to a `CString` it will return
460     /// Err(errSecConversionError).
copy_info<T: Into<Vec<u8>>>(&self, tag: Option<T>) -> Result<AuthorizationItemSet<'_>>461     pub fn copy_info<T: Into<Vec<u8>>>(&self, tag: Option<T>) -> Result<AuthorizationItemSet<'_>> {
462         let tag_with_nul: CString;
463 
464         let tag_ptr = match tag {
465             Some(tag) => {
466                 tag_with_nul = cstring_or_err!(tag)?;
467                 tag_with_nul.as_ptr()
468             }
469             None => std::ptr::null(),
470         };
471 
472         let mut inner = MaybeUninit::<*mut sys::AuthorizationItemSet>::uninit();
473 
474         let status =
475             unsafe { sys::AuthorizationCopyInfo(self.handle, tag_ptr, inner.as_mut_ptr()) };
476 
477         if status != sys::errAuthorizationSuccess {
478             return Err(Error::from(status));
479         }
480 
481         let set = AuthorizationItemSet {
482             inner: unsafe { inner.assume_init() },
483             phantom: PhantomData,
484         };
485 
486         Ok(set)
487     }
488 
489     /// Creates an external representation of an authorization reference so that
490     /// you can transmit it between processes.
make_external_form(&self) -> Result<sys::AuthorizationExternalForm>491     pub fn make_external_form(&self) -> Result<sys::AuthorizationExternalForm> {
492         let mut external_form = MaybeUninit::<sys::AuthorizationExternalForm>::uninit();
493 
494         let status =
495             unsafe { sys::AuthorizationMakeExternalForm(self.handle, external_form.as_mut_ptr()) };
496 
497         if status != sys::errAuthorizationSuccess {
498             return Err(Error::from(status));
499         }
500 
501         Ok(unsafe { external_form.assume_init() })
502     }
503 
504     /// Runs an executable tool with root privileges.
505     /// Discards executable's output
506     #[cfg(target_os = "macos")]
507     #[inline(always)]
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>,508     pub fn execute_with_privileges<P, S, I>(
509         &self,
510         command: P,
511         arguments: I,
512         flags: Flags,
513     ) -> Result<()>
514     where
515         P: AsRef<std::path::Path>,
516         I: IntoIterator<Item = S>,
517         S: AsRef<std::ffi::OsStr>,
518     {
519         use std::os::unix::ffi::OsStrExt;
520 
521         let arguments = arguments
522             .into_iter()
523             .map(|a| CString::new(a.as_ref().as_bytes()))
524             .flatten()
525             .collect::<Vec<_>>();
526         self.execute_with_privileges_internal(command.as_ref().as_os_str().as_bytes(), &arguments, flags, false)?;
527         Ok(())
528     }
529 
530     /// Runs an executable tool with root privileges,
531     /// and returns a `File` handle to its communication pipe
532     #[cfg(target_os = "macos")]
533     #[inline(always)]
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>,534     pub fn execute_with_privileges_piped<P, S, I>(
535         &self,
536         command: P,
537         arguments: I,
538         flags: Flags,
539     ) -> Result<File>
540     where
541         P: AsRef<std::path::Path>,
542         I: IntoIterator<Item = S>,
543         S: AsRef<std::ffi::OsStr>,
544     {
545         use std::os::unix::ffi::OsStrExt;
546 
547         let arguments = arguments
548             .into_iter()
549             .map(|a| CString::new(a.as_ref().as_bytes()))
550             .flatten()
551             .collect::<Vec<_>>();
552         Ok(self.execute_with_privileges_internal(command.as_ref().as_os_str().as_bytes(), &arguments, flags, true)?.unwrap())
553     }
554 
555     // Runs an executable tool with root privileges.
556     #[cfg(target_os = "macos")]
execute_with_privileges_internal( &self, command: &[u8], arguments: &[CString], flags: Flags, make_pipe: bool, ) -> Result<Option<File>>557     fn execute_with_privileges_internal(
558         &self,
559         command: &[u8],
560         arguments: &[CString],
561         flags: Flags,
562         make_pipe: bool,
563     ) -> Result<Option<File>>
564     {
565         use std::os::unix::io::{FromRawFd, RawFd};
566 
567         let c_cmd = cstring_or_err!(command)?;
568 
569         let mut c_args = arguments.iter().map(|a| a.as_ptr() as _).collect::<Vec<_>>();
570         c_args.push(std::ptr::null_mut());
571 
572         let mut pipe: *mut libc::FILE = std::ptr::null_mut();
573 
574         let status = unsafe {
575             sys::AuthorizationExecuteWithPrivileges(
576                 self.handle,
577                 c_cmd.as_ptr(),
578                 flags.bits(),
579                 c_args.as_ptr(),
580                 if make_pipe { &mut pipe } else { std::ptr::null_mut() },
581             )
582         };
583 
584         crate::cvt(status)?;
585         Ok(if make_pipe {
586             if pipe.is_null() {
587                 return Err(Error::from_code(32)); // EPIPE?
588             }
589             Some(unsafe { File::from_raw_fd(libc::fileno(pipe) as RawFd)})
590         } else {
591             None
592         })
593     }
594 }
595 
596 impl Drop for Authorization {
597     #[inline]
drop(&mut self)598     fn drop(&mut self) {
599         unsafe {
600             sys::AuthorizationFree(self.handle, self.free_flags.bits());
601         }
602     }
603 }
604 
605 #[cfg(test)]
606 mod tests {
607     use super::*;
608     use core_foundation::string::CFString;
609 
610     #[test]
test_create_default_authorization()611     fn test_create_default_authorization() {
612         Authorization::default().unwrap();
613     }
614 
615     #[test]
test_create_allowed_authorization() -> Result<()>616     fn test_create_allowed_authorization() -> Result<()> {
617         let rights = AuthorizationItemSetBuilder::new()
618             .add_right("system.hdd.smart")?
619             .add_right("system.login.done")?
620             .build();
621 
622         Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap();
623 
624         Ok(())
625     }
626 
627     #[test]
test_create_then_destroy_allowed_authorization() -> Result<()>628     fn test_create_then_destroy_allowed_authorization() -> Result<()> {
629         let rights = AuthorizationItemSetBuilder::new()
630             .add_right("system.hdd.smart")?
631             .add_right("system.login.done")?
632             .build();
633 
634         let auth = Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap();
635         auth.destroy_rights();
636 
637         Ok(())
638     }
639 
640     #[test]
test_create_authorization_requiring_interaction() -> Result<()>641     fn test_create_authorization_requiring_interaction() -> Result<()> {
642         let rights = AuthorizationItemSetBuilder::new()
643             .add_right("system.privilege.admin")?
644             .build();
645 
646         let error = Authorization::new(Some(rights), None, Flags::EXTEND_RIGHTS).unwrap_err();
647 
648         assert_eq!(error.code(), sys::errAuthorizationInteractionNotAllowed);
649 
650         Ok(())
651     }
652 
create_credentials_env() -> Result<AuthorizationItemSetStorage>653     fn create_credentials_env() -> Result<AuthorizationItemSetStorage> {
654         let set = AuthorizationItemSetBuilder::new()
655             .add_string(
656                 "username",
657                 option_env!("USER").expect("You must set the USER environment variable"),
658             )?
659             .add_string(
660                 "password",
661                 option_env!("PASSWORD").expect("You must set the PASSWORD environment varible"),
662             )?
663             .build();
664 
665         Ok(set)
666     }
667 
668     #[test]
test_create_authorization_with_bad_credentials() -> Result<()>669     fn test_create_authorization_with_bad_credentials() -> Result<()> {
670         let rights = AuthorizationItemSetBuilder::new()
671             .add_right("system.privilege.admin")?
672             .build();
673 
674         let env = AuthorizationItemSetBuilder::new()
675             .add_string("username", "Tim Apple")?
676             .add_string("password", "butterfly")?
677             .build();
678 
679         let error =
680             Authorization::new(Some(rights), Some(env), Flags::INTERACTION_ALLOWED).unwrap_err();
681 
682         assert_eq!(error.code(), sys::errAuthorizationDenied);
683 
684         Ok(())
685     }
686 
687     #[test]
test_create_authorization_with_credentials() -> Result<()>688     fn test_create_authorization_with_credentials() -> Result<()> {
689         if option_env!("PASSWORD").is_none() {
690             return Ok(());
691         }
692 
693         let rights = AuthorizationItemSetBuilder::new()
694             .add_right("system.privilege.admin")?
695             .build();
696 
697         let env = create_credentials_env()?;
698 
699         Authorization::new(Some(rights), Some(env), Flags::EXTEND_RIGHTS).unwrap();
700 
701         Ok(())
702     }
703 
704     #[test]
test_query_authorization_database() -> Result<()>705     fn test_query_authorization_database() -> Result<()> {
706         assert!(Authorization::right_exists("system.hdd.smart")?);
707         assert!(!Authorization::right_exists("EMPTY")?);
708 
709         let dict = Authorization::get_right("system.hdd.smart").unwrap();
710 
711         let key = CFString::from_static_string("class");
712         assert!(dict.contains_key(&key));
713 
714         let invalid_key = CFString::from_static_string("EMPTY");
715         assert!(!dict.contains_key(&invalid_key));
716 
717         Ok(())
718     }
719 
720     /// This test will only pass if its process has a valid code signature.
721     #[test]
test_modify_authorization_database() -> Result<()>722     fn test_modify_authorization_database() -> Result<()> {
723         if option_env!("PASSWORD").is_none() {
724             return Ok(());
725         }
726 
727         let rights = AuthorizationItemSetBuilder::new()
728             .add_right("config.modify.")?
729             .build();
730 
731         let env = create_credentials_env()?;
732 
733         let auth = Authorization::new(Some(rights), Some(env), Flags::EXTEND_RIGHTS).unwrap();
734 
735         assert!(!Authorization::right_exists("TEST_RIGHT")?);
736 
737         auth.set_right(
738             "TEST_RIGHT",
739             RightDefinition::FromExistingRight("system.hdd.smart"),
740             None,
741             None,
742             None,
743         )
744         .unwrap();
745 
746         assert!(Authorization::right_exists("TEST_RIGHT")?);
747 
748         auth.remove_right("TEST_RIGHT").unwrap();
749 
750         assert!(!Authorization::right_exists("TEST_RIGHT")?);
751 
752         Ok(())
753     }
754 
755     /// This test will succeed if authorization popup is approved.
756     #[test]
test_execute_with_privileges() -> Result<()>757     fn test_execute_with_privileges() -> Result<()> {
758         if option_env!("PASSWORD").is_none() {
759             return Ok(());
760         }
761 
762         let rights = AuthorizationItemSetBuilder::new()
763             .add_right("system.privilege.admin")?
764             .build();
765 
766         let auth = Authorization::new(
767             Some(rights),
768             None,
769             Flags::DEFAULTS
770                 | Flags::INTERACTION_ALLOWED
771                 | Flags::PREAUTHORIZE
772                 | Flags::EXTEND_RIGHTS,
773         )?;
774 
775         let file = auth.execute_with_privileges_piped("/bin/ls", &["/"], Flags::DEFAULTS)?;
776 
777         use std::io::{self, BufRead};
778         for line in io::BufReader::new(file).lines() {
779             let _ = line.unwrap();
780         }
781 
782         Ok(())
783     }
784 }
785