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