Apple - Authorization Services Programming Guide

本文翻译整理自:Authorization Services Programming Guide(更新日期:2011-10-19
https://developer.apple.com/library/archive/documentation/Security/Conceptual/authorization_concepts/01introduction/introduction.html#//apple_ref/doc/uid/TP30000995


文章目录

  • 一、授权服务编程指南简介
    • 1、本文件的组织
    • 2、另见
  • 二、授权概念
    • 1、授权书
    • 2、认证
    • 3、安全服务器
    • 4、权利
    • 5、政策数据库
    • 6、凭据缓存和身份验证对话框
    • 7、情景
      • 7.1 简单的、自我限制的应用程序
      • 7.2 分解应用
      • 7.3 安装人员
  • 三、授权服务任务
    • 1、在简单的、自我限制的应用程序中进行授权
      • 1.1 创建没有权限的授权引用
      • 1.2 请求授权
        • 创建授权权限集
        • 指定授权选项
        • 授权
        • 释放授权项数组
      • 1.3 发布授权引用
    • 2、在分解应用程序中授权
      • 2.1 在分解应用程序中使用授权服务
        • 创建授权引用
        • 请求预授权
          • 创建预授权权限集
          • 为预授权指定授权选项
          • 预授权
        • 创建外部授权引用
        • 调用助手工具
        • 发布授权引用
      • 2.2 在帮助工具中使用授权服务
        • 检索授权引用
        • 执行授权
        • 执行特权操作
        • 修复辅助工具
          • 将辅助工具称为root
          • 设置Setuid位
    • 3、调用特权安装程序
  • 词汇表


一、授权服务编程指南简介

重要提示:应用沙盒不支持授权服务API,因为它允许权限提升。

授权服务定义了一个编程接口,有助于对特权操作进行精细控制,例如访问操作系统的受限区域和Mac应用程序的自限制部分。
本文档介绍了如何使用授权服务来控制这些特权操作。

授权服务编程指南解释了授权背后的概念,并提供了如何使用授权服务的示例。

从使用授权服务中受益的产品类型包括

  • 调用系统受限工具的应用程序
  • 限制访问自己工具的软件
  • 安装特权工具或需要访问操作系统受限区域的软件安装程序

例如,您可以使用授权服务重新启动后台进程或访问受限目录,例如/Applications目录。
在这些情况下正确使用授权服务可以大大降低软件无意中损坏操作系统受限区域或允许未经授权的用户访问这些区域的可能性。

如果您的应用程序包含工具或执行您希望只有管理用户有权访问的操作,则它可以从授权服务中受益。

授权服务使用macOS中的身份验证机制。
如果macOS的未来版本支持额外的身份验证机制,现在采用授权服务将使您的应用程序能够利用这些机制,而无需更改代码。

注意:本文档以前的标题为使用授权服务执行特权操作。


1、本文件的组织

授权概念向您介绍macOS中的授权,并描述授权和身份验证之间的区别。
本章探讨使用授权服务的场景。
阅读本章以更好地了解您的软件是否可以从使用授权服务中受益。

授权服务任务详细解释了如何在自限制应用程序、系统限制应用程序和特权安装程序中使用授权服务。

术语表定义了本书中引入的新术语。


2、另见

有关API的详细信息,请参阅授权服务。


二、授权概念

重要提示:应用沙盒不支持授权服务API,因为它允许权限提升。

本章介绍概念,而不是实现或编程细节。
有关在应用程序中使用特定授权服务功能的信息,请参阅授权服务任务。

在阅读本章之前,您应该了解BSD和macOS中权限和所有权的基础知识。
有关这些概念的简要介绍,请参阅 安全概述
有关术语的定义,请参阅词汇表。


本章包含以下部分:

  • 授权提供了macOS使用的基于策略的授权的概念性概述。
  • 身份验证描述授权如何使用身份验证。
  • 安全服务器描述了如何在应用程序中使用授权服务与安全服务器进行交互。
  • 权限描述了如何命名您自己的权限。
  • 策略数据库说明安全服务器如何使用策略数据库做出授权决策。
  • 凭据缓存和身份验证对话框说明了安全服务器如何确定是否显示身份验证对话框。
  • 场景描述了使用授权服务的不同场景。

1、授权书

macOS内核的底层BSD部分提供了一个 user-and-owner-security 模型。
每个文件系统对象,例如文件或目录,都有一个所有者和一组权限或属性,指定所有者、一个组和所有其他人能够对该对象做什么。

在某些情况下,BSD安全模型不适合macOS用户面临的情况。
例如,如果您想创建一个grades-and-transcripts应用程序,您希望教师和学校注册员使用该应用程序,但您可能希望将成绩单的创建限制在注册员。

您可能需要保护用户 不意外地进行底层BSD安全模型 允许的重要更改。
例如,您可能希望只允许管理员更改application-specific首选项。
授权服务还可用于以root身份执行操作(也称为超级用户),例如重新启动守护进程。

在这些情况下,除了BSD权限之外,基于策略的安全模型还为您的应用程序提供了额外的重要功能。
在基于策略的系统中,用户请求授权——授予权利或特权的行为——以执行特权操作。
授权是通过代理执行的,因此用户不必信任应用程序的密码。
代理是用户交互界面——代表安全服务器操作——用于获取用户密码或其他形式的标识,这也确保了应用程序之间的一致性。
安全服务器——macOS中处理授权和身份验证的核心服务守护程序——确定是否没有人、每个人或只有某些用户可以执行特权操作。

授权为您提供了对授予用户权限以执行管理任务和其他特权操作的细粒度控制。
使用授权服务允许您限制应用程序的某些部分,添加额外的安全预防措施,并且仍然满足BSD安全模型。
您应该避免绕过BSD安全模型——例如,不要以root身份运行进程——除非您别无选择,在这种情况下,您应该限制所涉及的代码量。

注意:在基于策略的系统中,您有责任为您的用户请求授权。
您的应用程序应在每次特权操作之前立即授权。

在某些情况下,在您的应用程序实际需要执行这些操作之前,确定用户是否被授权执行特权操作是很有价值的。
例如,当系统偏好设置应用程序被锁定时,它需要用户提供名称和密码,然后才能允许用户更改任何设置。
当非管理员用户单击锁定按钮(参见图1-1)时,系统偏好设置应用程序执行预授权。
在需要授权之前,预授权确定用户的权限。
通过预授权,系统偏好设置可以防止用户为他们无权执行的操作自定义和选择选项。


图1-1未经授权的用户看到的系统偏好设置应用程序示例
在这里插入图片描述


图1-2显示了用户在成功预授权后看到的窗口。
但是,系统偏好设置应用程序仍然在执行任何特权操作之前立即执行授权。


图1-2预授权用户看到的系统偏好设置应用程序示例
在这里插入图片描述


2、认证

认证是验证用户身份的行为。
一个常见的误解是授权和认证是一回事;然而,认证只是授权过程的一部分。
正如授权中所讨论的,在用户被认证后,授权过程涉及确定用户拥有什么权限或特权。


图1-3显示了系统偏好设置应用程序中的身份验证示例。


图1-3系统偏好设置应用程序中的身份验证示例
在这里插入图片描述


为了进行身份验证,根据可用的硬件,用户通常输入用户名和密码,或者扫描指纹寻找触摸识别。
其他身份验证机制可能包括插入智能卡或其他类型的生物识别技术。

当您的应用程序请求用户授权时,您可以设置一个允许安全服务器与用户交互的选项。
这样做会告诉安全服务器根据需要向用户请求身份证明或同意以进行身份验证。


3、安全服务器

您的应用向安全服务器提供授权引用、授权权限集和授权选项。
然后,安全服务器通过以下方式执行身份验证:

  • 使用授权引用访问凭据。
  • 要求用户提供用于身份验证的用户名和密码。
  • 使用授权权限集中的权限在策略数据库中查找规则。
  • 使用凭据和授权选项来确定用户是否遵守规则并应被授予授权权限集中请求的权限。
  • 返回授予或拒绝授权权限的结果。

要在应用程序和安全服务器之间启动授权会话,您需要创建一个授权引用。
安全服务器使用授权引用访问授权会话。
您将授权引用传递给几乎每个授权服务函数。

当您请求授权时,您以授权选项的形式向安全服务器发送指令。
授权选项告诉安全服务器如何进行授权请求。
例如,您可以指定调用用于授权、部分授权或预授权。
您还可以指定是否要允许安全服务器与用户交互以执行身份验证,或请求同意。

要授权用户,您必须向安全服务器传递一个授权权限集,其中包含用户需要的权限,例如创建副本或重新启动守护程序的权限。
权限是应用程序代表用户请求的命名权限。

凭据是表示经过身份验证的用户的令牌,安全服务器将其存储为授权会话的一部分。
安全服务器使用这些凭据作为真实性证明。
凭据在设定的时间后过期。
您还可以在释放授权引用时强制它们过期。

安全服务器使用包含一组规则的策略数据库。
规则是确定谁应该被授权执行特定操作的一组属性。
安全服务器将规则与用户权限和身份验证凭据进行比较,以确定用户是否被授权执行特权操作。

授予的权限不会存储在授权会话中。
相反,每次执行授权时,安全服务器都会使用凭据——或者重新验证用户,或者如果凭据已过期,则重新请求同意——并查阅策略数据库中的适当规则以重新评估授权。


4、权利

当应用程序请求授权时,您将请求的权限(授权权限集)传递给安全服务器。
安全服务器会将您传递的权限与策略数据库中的密钥进行比较。
找到匹配项时,安全服务器会使用与密钥关联的规则来确定授权。
有关策略数据库的详细信息,请参阅策略数据库。

您必须创建应用程序使用的权限。
权限使用分层命名空间。
权限应以组织的反向域名开头。
然后,权限应指定应用程序的名称并变得更具体——例如,com.myOrganization.myProduct.myRight
特定于macOS的权限具有以system开头的正确名称。

注意:权限区分大小写。


您的权限应该代表对一个或一组目标的单个操作。
例如,权限可能代表重新启动守护程序的单个操作,例如com.myOrganization.myProduct.inetd.restart以重新启动Internet守护程序,或com.myOrganization.myProduct.daemons.restart以重新启动一组守护程序。

因为可以为同一用户请求多个权限,所以不需要创建表示操作组合的权限。
例如,在grades-and-transcripts应用程序中,如果您命名一个com.myOrganization.myProduct.transcripts.create 和 另一个com.myOrganization.myProduct.grades.edit,则不需要单独的权限 com.myOrganization.myProduct.createTranscriptsAndEditGrades

您为权限选择的名称应该对用户有意义。
例如,system.finder.trash.emptysystem.finder.trashDirectory.deleteFiles 可读性更强。


5、政策数据库

策略数据库包含安全服务器用来为用户授权权限的一组规则。
每个规则由一组属性组成。
这些规则在安装macOS时预先配置,但应用程序可能随时更改它们。
因为任何应用程序都可以更改数据库中的权限,所以您的应用程序必须考虑所有可能的情况。
表1-1描述了为规则定义的属性。

Mac应用程序的策略数据库中有一些特定规则。
策略数据库中还有一个通用规则,安全服务器将其用于没有特定规则的任何权限。

规则属性通用规则值描述
key密钥是规则的名称。
密钥使用与权限相同的命名约定。
安全服务器使用规则的密钥将规则与权限匹配。
通配符密钥以“.”结尾。
通用规则的键值为空。
与特定规则不匹配的任何权限都使用通用规则。
groupadmin用户必须是该组的成员。
sharedtrue如果设置为true,则安全服务器将用于获得此权限的凭据标记为共享。
安全服务器可以使用任何共享凭据来授权此权限。
为获得最大安全性,请将共享设置为false,以便安全服务器为一个应用程序存储的凭据不能被另一个应用程序使用。
timeout300此规则使用的凭据在指定的秒数内过期。
对于每次用户都必须进行身份验证或同意的最大安全性,请将超时设置为0

除非将新规则添加到策略数据库,否则您的权限始终与通用规则匹配。
使用AuthorizationRightSet函数在数据库中添加或编辑规则。
使用AuthorizationRightGet函数读取当前规则。
使用AuthorizationRightRemove函数删除规则。

要锁定未明确允许的所有特权操作,请通过将超时属性设置为0来更改通用规则。
要在用户获得授权后允许所有特权操作,请从通用规则中删除超时属性。
要防止应用程序共享权限,请将共享属性设置为false
要要求用户以员工组而不是管理组的成员身份进行身份验证,请将组属性设置为staff

作为安全服务器如何将权限与策略数据库中的规则匹配的示例,请考虑一个grades-and-transcripts应用程序。
该应用程序请求权限com.myOrganization.myProduct.transcripts.create
安全服务器在策略数据库中查找权限。
如果找不到完全匹配,安全服务器会查找通配符密钥设置为com.myOrganization.myProduct.transcripts.com.myOrganization.myProduct.com.myOrganization.com.-按此顺序-检查最长匹配。
如果没有通配符匹配,则安全服务器使用通用规则。
安全服务器向用户请求身份验证。
用户提供用户名和密码以作为组admin进行身份验证。
安全服务器根据用户身份验证或同意以及请求的权限创建凭据。
凭据指定其他应用程序可以使用它,安全服务器将过期时间设置为五分钟。

三分钟后,应用程序的子进程启动。
该子进程请求正确的 com.myOrganization.myProduct.transcripts.create
安全服务器找到凭证,看到它允许共享,并使用权限。
两分半钟后,同一子进程再次请求正确的 com.myOrganization.myProduct.transcripts.create,但权限已过期。
安全服务器通过查阅策略数据库并请求用户身份验证或同意,开始创建新凭证的过程。


6、凭据缓存和身份验证对话框

您可能会注意到,当您调用AuthorizationCreate函数或AuthorizationCopyRights函数来获取用户权限时,有时会出现一个身份验证对话框,有时会出现一个同意对话框,有时则没有出现任何对话框。
这种行为的原因与策略数据库中的设置以及安全服务器缓存用户凭据的方式有关。

凭据是安全服务器知道的关于特定用户的信息,例如特定用户输入了有效的用户名和密码。

对于每个登录会话,安全服务器维护一个全局凭据缓存和每个授权实例的凭据缓存(即,每次创建新的授权引用时)。
策略数据库中每个权限的规则指示经过身份验证的用户必须属于的组以及凭据被认为有效的时间。
该规则还可能指示要共享凭据。

当授权服务需要凭据来授予用户权限时,安全服务器会尝试从凭据缓存中获取凭据。
它首先查看与授权实例关联的凭据缓存。
如果凭据不在那里并且凭据被共享,它会在全局凭据缓存中查找。
只有当安全服务器在缓存中找不到凭据时,它才会尝试获取凭据,通常是通过显示身份验证或同意对话框。
(在某些情况下,安全服务器可能能够从其他来源(例如智能卡)获取凭据。)

如果安全服务器成功获得新的凭据,它会将其存储在与授权实例关联的凭据缓存中,如果规则指定应共享凭据,则将其存储在全局凭据缓存中。

如果权限的规则有超时属性,其值表示缓存的凭据对该权限适用多长时间(以秒为单位)。
值为0表示该凭据只能使用一次(即立即超时)。
如果缺少超时属性,只要登录会话持续,凭据就可以用来授予权限,除非该凭据被显式销毁。


例如,当作为管理员组成员的用户登录到系统时,用户的凭据(即他们输入了有效的管理员用户名和密码的事实)将保存在全局凭据缓存中。
然后,当该用户尝试修改系统首选项时,安全服务器会在缓存中找到凭据,并且不会显示身份验证对话框。

另一方面,如果用户使用非管理员用户名和密码登录并尝试修改系统首选项之一,安全服务器将无法从凭据缓存中获取所需的凭据。
因此,它会显示身份验证对话框。

同样的原则适用于任何需要凭证的应用程序:如果用户已通过一个应用程序的身份验证并且凭证已被共享,则另一个应用程序可以使用该凭证。

因此,对AuthorizationCopyRights的调用是否导致对话框取决于安全服务器是否已经缓存了所需的凭据。

保证请求权限时获得的凭证不与其他授权实例共享的唯一方法是销毁凭证。
为此,请使用标志kAuthorizationFlagDestroyRights调用AuthorizationFree函数。


7、情景

涉及授权服务的主要场景有三种:简单的自限制应用程序、分解应用程序和安装程序。


7.1 简单的、自我限制的应用程序

自限制应用程序要求某些功能只能由特定用户组访问。
在简单的自限制应用程序中,这种功能分离是在主应用程序中完成的。
在这种情况下,您使用授权服务,因为这种细粒度限制不能由BSD权限控制。

考虑一个grades-and-transcripts应用程序,它只允许注册人员创建成绩单,而应用程序的其余部分可供教师和注册人员使用。
当用户尝试创建成绩单时,应用程序使用授权服务来决定该用户是否可以执行该操作。


图1-4显示了一个简单的、自我限制的应用程序的流程图。
应用程序创建一个授权引用。
授权引用指的是与安全服务器的授权会话。
在执行任何特权操作(例如创建抄本)之前,应用程序立即代表用户请求授权。
如果需要,安全服务器请求用户的身份验证或同意。
如果授权成功,则执行特权操作。
当不再需要授权引用时,应用程序释放授权引用。

在大多数情况下,将特权操作分离到单独的帮助工具中是有益的。
有关如何将授权服务与帮助工具一起使用的更多信息,请参阅分解应用程序。

如果您只需要授权一次,例如,当您的应用程序首次启动时,您可以在不创建授权引用的情况下执行授权。
为此,您直接使用授权调用的结果。
因为在这种情况下,您没有创建授权引用,所以不必释放它。
授权部分更详细地描述了一次性授权。


图1-4简单、自限制应用程序的流程图
在这里插入图片描述


7.2 分解应用

分解应用程序是将特定任务委托给更小的、独立的工具的应用程序。
这些工具有时被称为辅助工具。
在简单的、自限制的应用程序中,特权代码在应用程序本身中,而在分解应用程序中,特权代码在辅助工具中。

您的应用程序执行的操作可能受到BSD安全模型的限制。
此类应用程序是受系统限制的应用程序。
例如,需要重新启动Internet守护程序(inetd)的应用程序必须具有root权限,但它以启动它的用户的权限运行。

建议您同时考虑自限制应用程序和系统限制应用程序。
考虑您的应用程序有两个好处。
第一个是更容易审计分解应用程序,因为特权操作由帮助工具在单独的进程中执行。
第二个是分解应用程序提供了更多的安全性。
在非分解应用程序中,您不仅必须相信代码中没有安全漏洞,而且您链接到的所有代码中也没有漏洞。


图1-5显示了分解应用程序的应用程序部分的流程图,而辅助工具的流程图如图1-6所示。


图1-5分解应用程序的应用程序部分流程图
在这里插入图片描述


应用程序首先创建授权引用,并在调用辅助工具之前立即向安全服务器请求预授权。
应用程序使用预授权的结果来确定用户是否有权在辅助工具中执行特权操作。
执行预授权可确保调用用户无权使用的辅助工具不会浪费资源和时间。


图1-6辅助工具的流程图
在这里插入图片描述


应用程序需要将授权引用传递给辅助工具。
因为您不能在两个进程之间传输授权引用本身,所以应用程序使用授权服务创建授权引用的外部、可转移形式以发送到其辅助工具。

助手工具使用授权服务从外部授权引用创建授权引用。
助手工具请求授权并使用结果来决定是否继续特权操作。

您必须将授权引用传递给辅助工具,以便授权对话框可以显示应用程序的路径而不是辅助工具的路径,并允许系统确定授权对话框是否应该具有键盘焦点。

您必须在任何特权操作之前立即执行授权,即使用户已经成功授权。
权限可能会过期,因此作为开发人员,您有责任确保用户使用所有必需的权限。
任何应用程序都可以修改策略数据库以设置权限可用的时间长度(请参阅策略数据库)。

注意:执行特权操作的能力受BSD安全模型控制,而不是授权服务。
权限到期不会阻止具有适当BSD权限的用户执行某些特权操作——您必须通过在特权操作之前立即授权将这种预防构建到您的代码中。

一些系统提供的实用程序使用授权服务。
例如,authopen实用程序使用授权服务打开特权文件。
如果您调用像authopen这样的实用程序,那么就没有必要编写自己的帮助工具。

一些特权操作需要特殊权限。
例如,重新启动Internet守护程序的应用程序必须具有root权限。
执行此操作有三种可能的方法,它们都有自己的问题:

  • 通过使用特殊的授权服务函数调用自身,使应用程序以root身份运行。
  • 设置应用程序的setuid位并将其所有者更改为root,然后使用特殊的授权服务功能。
  • 剔除执行特权操作的操作并将其放在单独的setuid工具中-一个设置了setuid位的工具-并将setuid工具的所有者设置为root。

第一个和第二个选项都是等待发生的安全漏洞。
当特权应用程序运行时,它会调用授权服务提供的特殊函数——AuthorizationExecuteWithPrivileges(有关更多详细信息,请参阅将帮助工具作为根调用)。
调用此函数会执行任何具有根权限的应用程序或工具,而不管应用程序或工具的所有者是谁。
这非常危险,因为应用程序的某些部分很容易被替换。

第二个选项是设置特权应用程序的setuid位,并将其所有者更改为root。
设置setuid位后,运行它的进程可以伪装成另一个用户。
将应用程序的setuid位和所有者设置为不同的用户,例如root,会使替换变得更加困难。
然而,以root身份运行代码非常危险,应该尽可能少地这样做。
在整个应用程序上设置setuid位尤其危险,因为您相信您的整个应用程序以及您的应用程序链接到的代码没有安全漏洞。

第三种情况是目前最好的。
应用程序被分成两个部分:一个控制所有图形用户交互界面元素和非特权操作的应用程序,另一个是只执行重启Internet守护程序所涉及的操作的帮助工具。
帮助工具的setuid位已设置,所有者设置为root。
正确的授权服务功能如前所述使用。
分解和设置setuid位不仅可以最大限度地降低风险,还可以更轻松地审计代码中的安全漏洞。

最后一种情况的一个问题是,当文件被复制时,setuid位设置会丢失。
因此,如果用户复制了您的setuid工具,则不再设置setuid位。
可以在setuid工具本身中重置setuid位。
图1-7显示了修复自己的setuid位的setuid工具的流程图。
您可能不希望用户能够复制应用程序。
如果是这种情况,您无需担心修复setuid位,只需让用户知道他们需要重新安装应用程序即可。


图1-7自修复辅助工具流程图
在这里插入图片描述


运行自修复助手工具时,它会检查是否传递了自修复标志。
如果未传递自修复标志,则读取应用程序传递的外部授权引用。

然后,自修复setuid工具检查它是否以root身份运行。
如果它以root身份运行,则执行授权,并根据结果执行特权操作。
如果自修复setuid工具不是以root身份运行,则使用AuthorizationExecuteWithPrivileges函数调用自身并将自修复标志传递给自己。

当新的自修复setuid工具进程启动时,它会检查是否传递了自修复标志。
如果传递了自修复标志,则自修复setuid工具恢复授权引用并修复setuid位。
自修复setuid工具以root身份运行后,它执行授权并继续作为普通辅助工具。


7.3 安装人员

并非所有安装程序都需要授权-只有那些需要特殊权限才能将文件复制到受限目录、更改受限文件或设置setuid位的安装程序才需要授权。

安装程序是一种特殊情况,因为与其他应用程序不同,安装程序通常只运行一次。
由于使用受限,授权服务提供了一个函数来调用您的安装程序以root权限运行。
由用户来确定安装程序是否来自受信任的来源。


图1-8显示了应用程序使用授权服务调用安装程序的流程图。
在这种情况下,应用程序创建授权引用并执行预授权。
如果用户成功预授权,则调用AuthorizationExecuteWithPrivileges函数以root权限执行您的安装程序。


图1-8调用特权安装程序的应用程序流程图

在这里插入图片描述


图1-9显示了在安装程序本身中执行的授权服务调用的流程图。
授权应该在任何特权操作之前执行。


图1-9安装程序授权服务调用的流程图。

在这里插入图片描述


三、授权服务任务

重要提示:应用沙盒不支持授权服务API,因为它允许权限提升。

本章为您可以使用授权服务完成的任务提供说明和代码示例。
您可以在自己的应用程序中调整这些示例

  • 限制对您自己的应用程序部分的访问
  • 呼叫系统实用程序
  • 编辑特权文件
  • 安装您的特权工具

一个简单的、自我限制的应用程序需要以最小的安全问题限制用户在应用程序自己的操作中——例如,一个grades-and-transcripts的应用程序可能只允许注册商创建转录本。
阅读在一个简单的、自我限制的应用程序中授权如果你有一个自我限制的应用程序。

如果您有一个分解应用程序-例如,必须以root身份执行操作的应用程序,例如重新启动守护程序-您应该阅读在简单的、自限制的应用程序中授权 和 在分解应用程序中授权。

如果安装程序必须执行特权操作,请阅读调用特权安装程序以查看使用授权服务的安装程序示例。

有关执行系统受限特权操作的示例应用程序,请参阅示例代码>安全性。


1、在简单的、自我限制的应用程序中进行授权

一个简单的自限制应用程序使用授权服务来执行以下部分中描述的任务:

  • 创建没有权限的授权引用
  • 请求授权
  • 发布授权引用

1.1 创建没有权限的授权引用

安全服务器使用授权引用来访问授权会话的状态,其中包括任何存储的凭据。
您的应用程序只需要一个授权引用。

您可以使用AuthorizationCreate函数为授权引用分配内存。
例2-1中的代码片段显示了对AuthorizationCreate函数的调用,该函数创建一个没有权限的授权引用。
如果不立即需要权限,则没有权限的授权引用很有用,但需要授权引用,以便它可以在应用程序的不同部分中使用。
例如,在grades-and-transcripts应用程序中,可能会在应用程序启动时创建授权引用,但在用户尝试创建转录本之前不会请求权限。

**注意:**尽管您可以在一次调用中创建授权引用并分配权限,但请记住,如果授权被拒绝,则不会创建该引用,后续使用它的尝试将失败。
因此,通常最好创建没有权限的授权引用,如例2-1所示,然后稍后调用AuthorizationCopyRights函数来确定或更改权限,如请求授权所示。


例2-1创建没有权限的授权引用

AuthorizationRef myAuthorizationRef;
OSStatus myStatus;
myStatus = AuthorizationCreate (NULL, kAuthorizationEmptyEnvironment,
            kAuthorizationFlagDefaults, &myAuthorizationRef);

AuthorizationCreate函数有四个参数,第一个是一个授权权限集,由于NULL,此时没有权限被授权。
第二个参数是授权环境,当前没有实现;传递kAuthorizationEmptyEnvironment
第三个参数是授权选项,传递常量kAuthorizationFlagDefaults是因为应用程序没有请求任何权限。
第四个参数是你声明的授权引用的地址。
返回时,授权引用引用当前的授权会话。
如果授权引用创建成功,函数返回errAuthorizationSuccess

请求授权 描述了如何使用AuthorizationCopyRightsAuthorizationCreate函数来请求授权。
当您的应用程序完成授权引用时,请使用AuthorizationFree函数,如发布授权引用中所述。


1.2 请求授权

创建授权引用后,您可以请求授权。
您的应用程序应在每个特权操作之前立即执行授权。
在grades-and-transcripts示例中,应用程序在创建副本之前立即请求授权。

当您的应用程序请求授权时,安全服务器可能会请求用户进行身份验证。
授权服务允许您充分利用安全服务器的身份验证插件架构为您处理身份验证。
身份验证可能使用指纹或智能卡,而不是用户名和密码,但您的应用程序代码保持不变。

[图1-3] 显示了安全服务器提供的身份验证对话框。
用户输入管理员用户名和密码,然后单击确定。
安全服务器然后使用用户名和密码对用户进行身份验证和授权。

授权需要创建授权权限集和授权选项,以便在调用函数AuthorizationCopyRightsAuthorizationCreate时使用。
在您的应用程序中,通过执行以下部分中描述的任务请求授权:

  • 创建授权权限集
  • 指定授权选项
  • 授权
  • 释放授权项数组

创建授权权限集

要为用户授权特定权限,您必须创建一个授权权限集,以通过AuthorizationCopyRightsAuthorizationCreate函数传递给安全服务器。
授权权限集由授权项数组和授权项数组中的项目数组成。
授权项数组包含有关应用程序请求的权限的信息。

授权项数组中的每个项由四个信息组成:

  • 权利的名称
  • 包含与右相关的可选数据的值
  • value字段的字节长度
  • 可选标志

例2-2显示了一个授权项数组的示例。
在大多数情况下,在为权限创建项时,您将value字段设置为NULL,将valuelengthflags字段设置为0
您应该将name字段设置为您请求的权限的名称。
有关命名您自己的权限的信息,请参阅权限。


例2-2创建授权项数组

AuthorizationItem myItems[2];
 
myItems[0].name = "com.myOrganization.myProduct.myRight1";
myItems[0].valueLength = 0;
myItems[0].value = NULL;
myItems[0].flags = 0;
 
myItems[1].name = "com.myOrganization.myProduct.myRight2";
myItems[1].valueLength = 0;
myItems[1].value = NULL;
myItems[1].flags = 0;

例如,grades-and-transcripts应用程序可能请求正确的com.myOrganization.myProduct.transcripts.create
valueLengthvalueflags字段将不被使用,并分别设置为0NULL和0。


例2-3显示了一个授权权限集的示例,在授权权限集中,count字段包含授权项数组中的权限数,而items字段指向您创建的授权项数组。


例2-3创建一组授权权限

AuthorizationRights myRights;
myRights.count = sizeof (myItems) / sizeof (myItems[0]);
myRights.items = myItems;

指定授权选项

您可以使用授权选项来指示安全服务器如何继续AuthorizationCopyRightsAuthorizationCreate函数。
通过设置授权选项,您可以使用这些函数

  • 授权部分权利
  • 授权所有权利
  • 预授权权

您可以在这些选项中包含与用户交互的选项。
安全服务器需要用户交互来执行身份验证。
最常见的组合授权所有权限并允许用户交互。
例2-4显示了授权选项的示例。


示例2-4为授权指定授权选项

AuthorizationFlags myFlags;
myFlags = kAuthorizationFlagDefaults |
            kAuthorizationFlagInteractionAllowed |
            kAuthorizationFlagExtendRights;

kAuthorizationFlagDefaults常量将位掩码归零。
kAuthorizationFlagExtendRights常量指示安全服务器授予权限。
如果没有此标志,AuthorizationCopyRightsAuthorizationCreate函数将返回相应的错误代码,但不会将权限扩展给用户。

如果您的应用程序不需要授权所有权限,您可以包含kAuthorizationFlagPartialRights常量来请求部分授权。
然后,您可以根据安全服务器授予的权限确定允许用户执行什么操作。
例2-5显示了为部分授权设置授权选项的示例。


例2-5为部分授权指定授权选项

myFlags = kAuthorizationFlagDefaults |
            kAuthorizationFlagInteractionAllowed |
            kAuthorizationFlagExtendRights |
            kAuthorizationFlagPartialRights;

请参阅请求预授权以了解要为预授权设置哪些授权选项。


授权

在例2-6中的代码片段显示了基于您创建的授权引用和授权权限集以及您指定的授权选项对AuthorizationCopyRights函数的调用。
在grades-and-transcripts示例中,AuthorizationCopyRights函数用于授权创建脚本的权限。


例2-6授权权限

myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights,
        kAuthorizationEmptyEnvironment, myFlags, NULL);

第一个参数是在创建无权限授权引用中创建的授权引用。
第二个参数是在创建授权权限集中创建的授权权限集。
第三个参数是授权环境。
授权环境当前未实现,所以传递kAuthorizationEmptyEnvironment
第四个参数是在指定授权选项中设置的授权选项。

第五个参数在授权部分权限时很有用,如例2-7所示。
该参数指向您声明的一个空的授权权限集。
返回时,它由安全服务器实际授权的权限组成。
如果您创建一个指向授权权限集的指针,那么您应该按照释放一个授权项数组中的说明释放它。


例2-7授权部分权限

AuthorizationRights *myAuthorizedRights;
myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights,
            kAuthorizationEmptyEnvironment, myFlags,
            &myAuthorizedRights);

如果安全服务器授予所有权限,AuthorizationCopyRights函数将返回errAuthorizationSuccess
您可以使用返回状态来确定用户是否可以执行特权操作。

您可以使用授权权限集和授权选项在创建授权引用时请求授权。

**注意:**尽管您可以在一次调用中创建授权引用并分配权限,如例2-8所示,但请记住,如果授权被拒绝,则不会创建该引用,后续使用它的尝试将失败。
因此,通常最好创建没有权限的授权引用,如例2-1所示,然后稍后调用AuthorizationCopyRights函数来确定或更改权限,如本节前面的代码示例所示。


例2-8显示了一个将授权与AuthorizationCreate函数相结合的示例。


例2-8创建具有权限的授权引用

myStatus = AuthorizationCreate (&myRights, kAuthorizationEmptyEnvironment,
            myFlags, &myAuthorizationRef);

您还可以使用AuthorizationCreate函数对用户进行一次性特权操作的授权。
如果应用程序在运行时只需要授权一次,那么一次性授权非常有用。
例2-9展示了一个示例,说明如何在不生成授权引用的情况下将授权权限集和授权选项与AuthorizationCreate函数一起使用。
传递NULL而不是授权引用。


例2-9一次性授权调用

myStatus = AuthorizationCreate (&myRights, kAuthorizationEmptyEnvironment,
            myFlags, NULL);


释放授权项数组

完成例2-7中设置的授权项后,调用AuthorizationFreeItemSet函数,如例2-10所示,释放它使用的内存。
仅在安全服务器分配的授权项数组上使用此函数,例如AuthorizationCopyRightsAuthorizationCopyInfo函数中使用的那些。


例2-10释放授权项数组

myStatus = AuthorizationFreeItemSet (myAuthorizedRights);


1.3 发布授权引用

在退出您的应用程序之前,或者在您希望结束当前授权会话的任何时候,调用AuthorizationFree函数来释放授权引用。
例如,grades-and-transcripts应用程序会等到用户退出应用程序后再释放授权引用。
每次用户创建抄本时使用相同的授权引用允许安全服务器重用任何尚未过期的共享凭据。
相比之下,用户单击网络首选项窗格中的打开锁定按钮等操作可以触发授权引用的释放,要求用户在单击关闭锁定按钮时重新授权。


例2-11中的代码段显示了使用AuthorizationFree函数的示例。
您必须传递授权引用和授权选项。
对于授权选项,如果要撤销与当前进程关联的凭据,则传递常量kAuthorizationFlagDefaults,或者传递常量kAuthorizationFlagDestroyRights以从所有使用它们的进程释放所有共享凭据。


例2-11发布授权引用

myStatus = AuthorizationFree (myAuthorizationRef,
            kAuthorizationFlagDestroyRights);

2、在分解应用程序中授权

分解应用程序,无论是系统限制的还是自限制的,都使用应用程序来控制图形用户交互界面和非特权操作,并使用单独的帮助工具来执行特权操作。

阅读在分解应用程序中使用授权服务,了解在分解应用程序中使用授权服务的说明,在助手工具中使用授权服务,了解在助手工具中使用授权服务的说明。


2.1 在分解应用程序中使用授权服务

您可以在分解应用程序中使用授权服务来执行以下部分中描述的任务:

  • 创建授权引用
  • 请求预授权
  • 创建外部授权引用
  • 调用助手工具
  • 发布授权引用

分解应用程序的一个示例是重新启动Internet守护程序的应用程序。
当帮助工具重新启动守护程序时,应用程序执行所有非特权操作。
应用程序创建授权引用并预先授权重新启动Internet守护程序的权利。
应用程序使用结果来确定是否启动帮助工具。
应用程序创建授权引用的外部版本并将其传递给帮助工具。
当不再需要授权引用时,应用程序释放它。


创建授权引用

在分解应用程序中创建授权引用与在简单的自限制应用程序中创建授权引用相同。
请参阅创建没有权限的授权引用以了解如何创建授权引用。


请求预授权

在调用辅助工具之前,您应该预先授权权限。
使用预先授权的结果,您可以防止未经授权的用户调用辅助工具。
这样做可以节省启动新进程和使用资源的时间,也可以节省用户准备执行他们没有权限执行的操作的时间。

预授权需要创建授权权限集和授权选项,以便在调用函数AuthorizationCopyRightsAuthorizationCreate时使用。
在您的应用程序中,您可以通过执行以下部分中描述的步骤对用户进行预授权:

  • 创建预授权权限集
  • 为预授权指定授权选项
  • 预授权

创建预授权权限集

预授权权限集与创建授权权限集中描述的授权权限集相同。


为预授权指定授权选项

预授权的授权选项类似于指定授权选项中描述的授权和部分授权的授权选项。
预授权和授权之间的唯一区别是,您不是使用结果来确定用户是否可以执行特权操作。
相反,您应该使用结果来确定用户是否可以在以后被授权。


例2-12显示了为预授权设置授权选项的示例。
kAuthorizationFlagDefaults常量将位掩码清零。
kAuthorizationFlagExtendRights常量告诉安全服务器扩展授予用户的任何权限。
kAuthorizationFlagInteractionAllowed常量告诉安全服务器它可以与用户交互以进行身份验证。
kAuthorizationFlagPreAuthorize常量告诉安全服务器对请求的权限进行预授权。


例2-12为预授权指定授权选项

AuthorizationFlags myFlags;
myFlags = kAuthorizationFlagDefaults |
            kAuthorizationFlagExtendRights |
            kAuthorizationFlagInteractionAllowed |
            kAuthorizationFlagPreAuthorize;

预授权

调用AuthorizationCopyRightsAuthorizationCreate函数对于预授权和授权是一样的。
授权部分中的示例参见例2-6。


创建外部授权引用

创建授权引用和预授权权限后,您需要将授权引用传递给您的帮助工具。
共享授权引用允许帮助工具使用作为分解应用程序授权会话一部分的任何凭据。
当您将授权引用传递给帮助工具时,授权对话框可以显示您的应用程序的路径,而不是帮助工具的路径。
它还使系统能够确定授权对话框是否应该具有键盘焦点。

授权引用的一个问题是它不是可以从一个进程转移到另一个进程的形式。
为了解决这个问题,授权服务提供了一个功能,可以将授权引用转换为可以传递给助手工具的外部授权引用。

要创建外部授权引用,声明一个AuthorizationExternalForm类型的变量,并将其与现有授权引用一起传递给AuthorizationMakeExternalForm函数。
返回时,该变量包含授权引用的可转移形式。
例2-13显示了创建外部授权引用的示例。


例2-13创建外部授权引用

AuthorizationExternalForm myExternalAuthorizationRef;
myStatus = AuthorizationMakeExternalForm (myAuthorizationRef,
            &myExternalAuthorizationRef);

阅读“Retrieving an Authorization Reference”,了解如何从助手工具中的外部授权引用中检索授权引用。


调用助手工具

当您准备好调用帮助工具时,使用某种形式的进程间通信(例如通信管道)将外部授权引用传递给工具。


发布授权引用

释放分解应用程序中的授权引用与释放授权引用中描述的相同。


2.2 在帮助工具中使用授权服务

您可以在帮助工具中使用授权服务来执行以下部分中描述的任务:

  • 检索授权引用
  • 执行授权
  • 执行特权操作

例如,重新启动Internet守护程序的辅助工具从应用程序传递的外部授权引用中检索授权引用。
然后辅助工具在重新启动Internet守护程序之前立即请求授权。

如果您的辅助工具实际上是一个自我修复辅助工具,您还应该阅读修复一个辅助工具。


检索授权引用

为了共享授权会话,分解应用程序将外部授权引用传递给帮助器工具(请参阅创建外部授权引用)。
在帮助器工具中,您使用AuthorizationCreateFromExternalForm函数从外部授权引用中检索授权引用。


例2-14显示了一个使用AuthorizationCreateFromExternalForm函数的示例。
在此示例中,外部授权引用是从辅助工具进程和父进程之间的通信管道中读取的。
然后,您将外部授权引用传递给函数AuthorizationCreateFromExternalForm
返回时,myAuthorizationRef是授权引用。


例2-14检索授权引用

AuthorizationRef myAuthorizationRef;
AuthorizationExternalForm myExternalAuthorizationRef;
OSStatus myStatus;
 
/* *** You should read in the external authorization reference into
        myExternalAuthorizationRef here. *** */
 
myStatus = AuthorizationCreateFromExternalForm (&myExternalAuthorizationRef,
            &myAuthorizationRef);


执行授权

在助手工具中执行授权与简单的自限制应用程序相同。
有关详细信息,请参阅请求授权。


执行特权操作

您应该使用授权的结果来确定是否允许用户执行特权操作。
实际执行特权操作不需要授权服务功能。


修复辅助工具

**重要提示:**有关此处记录的方法的更新和更安全的替代方法,请参阅更好的授权示例(BetterAuthorizationSample)中的示例代码,该代码使用启动守护程序来启动辅助工具,而不是设置setuid位。

如果您的辅助工具需要以root身份运行以执行特权操作,例如重新启动Internet守护程序,那么它应该设置其setuid位。
设置了setuid位的工具(有时称为setuid工具)必须是Mach-O二进制文件,因为CFM二进制文件不支持setuid或setgid(设置组标识符)位。
安装程序时,安装程序应设置辅助工具的setuid位,并将其所有者设置为root。

在OS X 10.1及更早版本中,当用户将setuid工具移动到另一个卷,或将其从一个地方复制到另一个卷时,setuid位会被文件系统重置,并且组和所有者会更改以匹配移动setuid工具的用户。
这样做是为了通过允许任何用户以root身份运行setuid工具来降低setuid工具带来的安全风险。
另一方面,大多数用户期望当他们将应用程序或工具从一个文件夹复制到另一个文件夹时,它仍然可以工作。
因此,setuid位、组和所有者需要重置,而无需编辑终端窗口中的权限。
本节提供代码以允许您的setuid工具在出现此问题时修复其自己的setuid位。

注意:在OS X 10.1中,复制或移动的setuid工具会丢失其setuid位。
所有者和组会更改以匹配执行操作的用户的权限。
在以后的版本中,用户可以移动setuid工具并保留权限集。

所有setuid工具都是潜在的安全问题。
这种情况带来了一个特殊的问题,因为即使用户篡改了setuid工具的代码,该工具也会自我修复其setuid位。
作为额外的安全性,您可能希望在执行此操作时向用户显示警告,以便他们决定继续或取消自我修复操作,或者可能强制用户从安装程序重新安装应用程序。

您可以通过执行以下部分中描述的任务来修复辅助工具上的setuid位:

  • 将辅助工具称为root
  • 设置Setuid位

将辅助工具称为root

要使辅助工具设置自己的setuid位,该工具必须具有root权限。
这是一个循环问题,因为除非您的辅助工具已经以root身份运行,否则您无法更改辅助工具的权限。
这就是函数AuthorizationExecuteWithPrivileges发挥作用的地方。

AuthorizationExecuteWithPrivileges函数通过特殊的安全过程以root身份执行任何应用程序。
例2-15中的代码示例演示了帮助工具如何以root权限递归调用自己,以便修复自己的setuid位。

注意:AuthorizationExecuteWithPrivileges函数仅将工具的有效用户ID(EUID)设置为root。
工具的真实用户ID(RUID)是调用者的用户ID。

**重要提示:**由于存在安全风险,建议仅在特殊且不经常使用的情况下调用AuthorizationExecuteWithPrivileges函数,例如修复setuid位和安装应用程序。
在最安全的计算机系统中,此函数请求的权利会立即超时,因此每次调用它时,用户都必须进行身份验证。


例2-15以root权限执行帮助工具

FILE *myCommunicationsPipe = NULL;
char *myArguments[] = {"--self-repair", NULL};
char myPath[MAXPATHLEN];
 
/* *** You should determine the path of your tool here and put the result in
        myPath. *** */
 
myStatus = AuthorizationExecuteWithPrivileges (myAuthorizationRef,
            myPath, kAuthorizationFlagDefaults, myArguments,
            &myCommunicationsPipe);

AuthorizationExecuteWithPrivileges函数期望您传递五个参数。
第一个参数是您检索到的授权引用,如检索授权引用。
授权引用允许辅助工具使用作为分解应用程序授权会话一部分的任何凭据。
第二个参数是正在调用的辅助工具的完整POSIX路径名——在本例中为setuid工具。
第三个参数是授权选项。
在此函数中,此参数未实现,因此目前将其设置为kAuthorizationFlagDefaults
第四个参数是被调用工具的以null结尾的参数数组。
您可以使用此参数将所需的任何信息从父进程传递给子进程。
在这种情况下,字符串"--self-repair"被传递给帮助器工具以指示它应该执行例2-16中的代码。
第五个参数是一个通信管道,因此帮助器工具可以将它从分解应用程序接收到的数据传递给它自己。

**重要提示:**您可能会AuthorizationExecuteWithPrivileges使用该函数来执行特权操作,而不是创建和调用您自己的setuid工具。
尽管这看起来像是一个简单的解决方案,但使用AuthorizationExecuteWithPrivileges函数而不使用其余授权服务函数会产生严重的安全漏洞,因为该函数会以root用户身份不加选择地运行任何工具。
Setuid工具也存在安全风险,但它们远没有将该函数用于AuthorizationExecuteWithPrivileges本文档中描述的目的之外的目的那么严重。
阅读分解应用程序以获取有关创建您自己的助手工具的说明。


设置Setuid位

在例2-15中,helper工具递归调用自身,传递自我修复参数--self-repair
因此,在同一个helper工具中,您需要检查自我修复参数,如果找到,则修复setuid位。
有关示例自我修复setuid工具,请参阅More Is Better示例代码(MoreIsBetter)。

**注意:**这里讨论的辅助工具中的自我修复代码的目的是允许工具在用户移动或复制工具后以root身份执行,即使文件系统已经重置了setuid位并更改了所有者和组以匹配执行该操作的用户的权限。
如果setuid位已经被清除,则此自我修复代码(即对AuthorizationExecuteWithPrivileges函数的调用)有效,无论该工具是由root拥有还是由用户拥有。
如果该工具设置了setuid位并且由root拥有,则不调用自我修复代码。
但是,如果设置了setuid位并且工具归用户所有,则对AuthorizationExecuteWithPrivileges函数的调用失败,因为在这种情况下尊重setuid位,并且工具以用户权限而不是root权限执行。
如果您希望您的自修复助手工具处理这种不太可能的情况,您需要在调用自修复代码之前添加代码来清除setuid位。

调用AuthorizationExecuteWithPrivileges函数时,需要一种方法来检索在调用中传递的授权引用。
例2-16显示了使用AuthorizationCopyPrivilegedReference函数检索授权引用的代码。
使用此函数的唯一时间是检索通过调用AuthorizationExecuteWithPrivileges传递的授权引用。

调用AuthorizationCopyPrivilegedReference函数的第一个参数是您声明的空授权引用。
您不应该调用AuthorizationCreate函数。
返回时,授权引用指向原始授权引用的副本。
第二个参数没有实现,因此将其设置为kAuthorizationFlagDefaults


例2-16设置setuid位

myStatus = AuthorizationCopyPrivilegedReference (&myAuthorizationRef,
            kAuthorizationFlagDefaults)


3、调用特权安装程序

有时,安装程序必须将文件安装在运行安装程序的用户不拥有的目录中。
这应该是一种罕见的情况,您应该尽可能避免它。
如果无法避免,例2-17中的代码显示了一个使用可选标志-un运行/usr/bin/id实用程序的工具。
通过替换实用程序路径并包含您自己的标志,您可以使用此示例代码以root权限调用您的安装程序。
然后,您的安装程序将能够执行它需要的任何特权操作。


例2-17调用特权安装程序

#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
 
int read (long,StringPtr,int);
int write (long,StringPtr,int);
 
int main() {
 
    OSStatus myStatus;
    AuthorizationFlags myFlags = kAuthorizationFlagDefaults;              // 1
    AuthorizationRef myAuthorizationRef;                                  // 2
 
    myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,  // 3
                myFlags, &myAuthorizationRef);
    if (myStatus != errAuthorizationSuccess)
        return myStatus;
 
    {
        AuthorizationItem myItems = {kAuthorizationRightExecute, 0,    // 4
                NULL, 0};
        AuthorizationRights myRights = {1, &myItems};                  // 5
 
        myFlags = kAuthorizationFlagDefaults |                         // 6
                kAuthorizationFlagInteractionAllowed |
                kAuthorizationFlagPreAuthorize |
                kAuthorizationFlagExtendRights;
        myStatus = AuthorizationCopyRights (myAuthorizationRef,        // 7
                                     &myRights, NULL, myFlags, NULL );
    }
 
    if (myStatus != errAuthorizationSuccess) goto DoneWorking;
 
    {
        char myToolPath[] = "/usr/bin/id";
        char *myArguments[] = { "-un", NULL };
        FILE *myCommunicationsPipe = NULL;
        char myReadBuffer[128];
 
        myFlags = kAuthorizationFlagDefaults;                          // 8
        myStatus = AuthorizationExecuteWithPrivileges                  // 9
                (myAuthorizationRef, myToolPath, myFlags, myArguments,
                &myCommunicationsPipe);
 
        if (myStatus == errAuthorizationSuccess)
            for(;;)
            {
                int bytesRead = read (fileno (myCommunicationsPipe),
                        myReadBuffer, sizeof (myReadBuffer));
                if (bytesRead < 1) goto DoneWorking;
                write (fileno (stdout), myReadBuffer, bytesRead);
            }
    }
 
    DoneWorking:
 
    AuthorizationFree (myAuthorizationRef, kAuthorizationFlagDefaults); // 10
 
    if (myStatus) printf("Status: %ld\n", myStatus);
    return myStatus;
}

以下是示例2-17中编号代码行的解释:

  1. 声明一个变量来存储授权选项。
  2. 声明授权引用。
  3. 使用AuthorizationCreate函数初始化授权引用。
    有关详细信息,请参阅创建没有权限的授权引用。
  4. 创建授权项数组。
    用户必须具有执行权限才能使用AuthorizationExecuteWithPrivileges函数。
    要创建执行授权项的权限,请将name字段设置为kAuthorizationRightExecute,将value字段设置为NULL,将valueLengthflags字段设置为0
    有关详细信息,请参阅创建授权权限集。
  5. 创建授权权限集。
    count字段设置为授权项数组中的项目数,并将items字段设置为指向授权项数组。
    有关详细信息,请参阅创建授权权限集。
  6. 设置授权选项以预授权权限。
    有关详细信息,请参阅为预授权指定授权选项。
  7. 使用AuthorizationCopyRights函数预授权以root身份执行安装程序的权限。
    在这种情况下,如果用户无法预授权,则没有理由继续。
    有关详细信息,请参阅授权。
  8. AuthorizationExecuteWithPrivileges函数的授权选项设置为kAuthorizationFlagDefaults
    其他授权选项,例如kAuthorizationFlagInteractionAllowed常量指定的选项,是不必要的,因为无论您是否指定该选项,AuthorizationExecuteWithPrivileges函数都会与用户交互。
  9. 使用AuthorizationExecuteWithPrivileges函数调用您的安装程序。
    在第一个参数中传递授权引用。
    在第二个参数中传递安装程序的完整POSIX路径名。
    在第三个参数中传递默认的授权选项。
    在第四个参数中传递安装程序的任何参数。
    可以通过第五个参数设置到工具的通信管道。
    有关AuthorizationExecuteWithPrivileges函数的更多信息,请参阅调用辅助工具作为根。
  10. 使用AuthorizationFree函数释放授权引用。
    有关详细信息,请参阅释放授权引用。

词汇表

  • administrator | 管理员
    管理员组中的用户。安装OS X的用户将自动分配到管理员组。管理员的权限少于root用户,但多于普通用户。管理员不能在系统域中创建、删除或移动文件。
  • authentication | 身份验证
    用用户拥有、知道或正在使用的东西验证身份的行为。例如,用户知道姓名和密码等信息。用户可能有物理的东西,如智能卡。身份可以是用户的东西——指纹或视网膜扫描等物理特征。身份验证可能需要两种或两种以上的身份证明形式。
  • authorization | 授权
    授予权限的行为。例如,用户请求执行操作的权限。安全服务器在用户满足策略数据库中指定的规则(例如提供凭据或身份验证)后授予授权。
  • authorization option | 授权选项
    指示安全服务器如何处理请求的参数或字段。选项包括请求预授权、请求部分授权、附加权限以及与用户交互。
  • authorization reference | 授权引用
    安全服务器使用授权引用访问与进程关联的授权会话。
  • Authorization Services| 授权服务
    一种API,有助于对特权操作进行精细控制,例如访问操作系统的受限区域和Mac应用程序的自限制部分。安全服务器使用基于策略的决策为用户授权权限。
  • biometric identifier | 生物识别器
    用于识别的生物物质的测量——例如指纹、视网膜扫描和面部识别。
  • credential | 凭据
    用户身份验证的证明。由安全服务器使用。当安全服务器对用户进行身份验证时,它会创建一个凭据作为授权会话的一部分。
  • factored application | 分解应用程序
    使用辅助工具执行特定任务的应用程序。进程间通信机制用于进程之间的通信。在使用授权服务的分解应用程序中,执行特权操作的代码被分解到单独的辅助工具中。
  • helper tool | 辅助工具
    作为单独进程执行应用程序某些功能的工具。在安全的情况下,辅助工具为应用程序执行特权操作。另请参见setuid工具。
  • key
    规则的名称。安全服务器使用规则的密钥将权限与规则匹配。
  • permissions | 权限
    在BSD中,一组控制谁可以读取、写入和执行文件系统中的资源的属性。ls -l命令的输出将权限表示为九位代码,分为三个二进制三字符子代码;第一个子代码为文件所有者提供权限,第二个子代码为文件所属的组提供权限,最后一个子代码为其他所有人提供权限。例如,-rwsr-xr--表示文件所有者具有读取、写入、执行权限(rwx);该组具有读取和执行权限(r-x);所有其他只有读取权限。(最左边的位置为一个特殊字符保留,该字符表示这是一个常规文件(-)、一个目录(d)、一个符号链接(l)还是一个特殊的伪文件设备。)执行位对目录有不同的语义,这意味着它们是可搜索的。
  • policy-based system | 基于策略的系统
    需要授权才能执行特权操作的系统。
  • policy database | 策略数据库
    包含安全服务器用于确定授权的规则集的数据库。
  • preauthorization | 预授权
    在执行实际授权之前使用的一种授权形式。预授权用于确定用户以后是否有可能进行授权。
  • privileged operation | 特权操作
    需要特殊权限或权限的操作。例如,用户以root身份执行的所有操作都具有特权。
  • right
    命名特权。安全服务器授权用户执行特权操作的权限。
  • rule | 规则
    用于为应用程序和系统设置安全策略的一组属性。另请参见策略数据库。
  • root
    (1)具有无限系统权限的用户。也称为超级用户。
    (2)BSD样式目录层次结构中的顶部目录。写成斜杠(/),它是每个绝对路径名中的第一个元素。
  • Security Server | 安全服务器
    OS X中的核心服务应用程序,通过与策略数据库和可插拔身份验证模块(PAM)的交互来处理授权和身份验证。
  • self-restricted application | 自限制应用程序
    将其部分功能限制为特定用户的应用程序。
  • setuid bit
    资源权限代码中的第四位。当该位设置为s时,系统允许运行它的进程伪装成另一个用户。例如,-r-sr-xr-x 1 root wheel traceroute允许运行traceroute的进程以root身份运行。
  • setuid 工具
    设置了setuid位的工具。
  • 系统受限应用程序
    由于BSD权限系统,其部分功能仅限于特定用户的应用程序。

2024-06-16(日)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/725681.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

探究布局模型:从LayoutLM到LayoutLMv2与LayoutXLM

LAYOUT LM 联合建模文档的layout信息和text信息&#xff0c; 预训练 文档理解模型。 模型架构 使用BERT作为backbone&#xff0c; 加入2-D绝对位置信息&#xff0c;图像信息 &#xff0c;分别捕获token在文档中的相对位置以及字体、文字方向、颜色等视觉信息。 2D位置嵌入 …

天地图 uniapp使用笔记

官网地址&#xff1a;天地图API 效果&#xff1a; <template><view><!-- 显示地图的DOM节点 --><view id"container" class"content"></view><!-- END --><!-- 数据显示 --><h3>城市名称(IP属地)&#x…

rollup学习笔记

一直使用的webpack,最近突然想了解下rollup,就花点时间学习下. 一,什么是rollup? rollup 是一个 JavaScript 模块打包器&#xff0c;可以将小块代码编译成大块复杂的代码,比如我们的es6模块化代码,它就可以进行tree shaking,将无用代码进行清除,打包出精简可运行的代码包. 业…

[Linux] 系统管理

全局配置文件 用户个性化配置 配置文件的种类 alias命令和unalias命令 进程管理 进程表

AI视频智能监管赋能城市管理:打造安全有序的城市环境

一、方案背景 随着城市化进程的加速和科技的飞速发展&#xff0c;街道治安问题日益凸显&#xff0c;治安监控成为维护社会稳定和保障人民安全的重要手段。当前&#xff0c;许多城市已经建立了较为完善的治安监控体系&#xff0c;但仍存在一些问题。例如&#xff0c;监控设备分…

基于PHP的奶茶商城系统

有需要请加文章底部Q哦 可远程调试 基于PHP的奶茶商城系统 一 介绍 此奶茶商城系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;ajax实现数据交换。系统角色分为用户和管理员。系统在原有基础上添加了糖度的选择。 技术栈 phpmysqlajaxphpstudyvscode 二 功能 用户…

深入了解RTMP推流技术:视频汇聚EasyCVR低延迟与高稳定性分析

RTMP&#xff08;Real Time Messaging Protocol&#xff09;视频推流技术&#xff0c;作为音视频传输领域的关键技术之一&#xff0c;已经在直播、视频会议、在线教育等多个场景中得到了广泛应用。RTMP以其独特的优势&#xff0c;为实时音视频传输提供了高效、稳定的解决方案。…

前端框架中的路由(Routing)和前端导航(Front-End Navigation)

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介前端框架中的路由&#xff08;Routing&#xff09;和前端导航&#xff08;Front-End Navigation&#xff09;1. 路由&#xff08;Routing&#xff09;1.1 定义1.2 路由的核心概念1.2.1 路由表&#xff08;Route Table&#xff09;1…

Renesas MCU之IO应用介绍

目录 概述 1 软硬件环境 1.1 软件版本信息 1.2 硬件接口介绍 2 FSP配置项目 2.1 项目参数配置 2.2 生成项目框架 3 IO OutPut功能 3.1 IO输出功能实现 3.2 IO输出功能测试代码 4 IO InPut功能 4.1 IO Input功能实现 4.2 测试代码实现 5 测试结果 概述 本文主要…

springboot应用cpu飙升的原因排除

1、通过top或者jps命令查到是那个java进程&#xff0c; top可以看全局那个进程耗cpu&#xff0c;而jps则默认是java最耗cpu的&#xff0c;比如找到进程是196 1.1 top (推荐)或者jps命令均可 2、根据第一步获取的进程号&#xff0c;查询进程里那个线程最占用cpu&#xff0c;发…

Redis的缓存击穿与解决

缓存击穿问题也叫热点Key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的Key突然失效了&#xff0c;无数的请求访问会在瞬间给数据库带来巨大的冲击。 Redis实战篇 | Kyles Blog (cyborg2077.github.io) 目录 解决方案 互斥锁 实现 逻辑过期 实现 解决方案…

FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg

《FFmpeg开发实战&#xff1a;从零基础到短视频上线》一书的“第11章 FFmpeg的桌面开发”介绍了如何在Windows环境对Qt结合FFmpeg实现桌面程序&#xff0c;那么Windows系统通过Visual Studio开发桌面程序也是很常见的&#xff0c;下面就介绍如何在Visual Studio的C工程中集成F…

Day13—大语言模型

定义 大语言模型&#xff08;Large Language Models&#xff09;是一种基于深度学习的自然语言处理&#xff08;NLP&#xff09;模型&#xff0c;用于处理和生成人类语言文本。 一、认识NLP 什么是NLP ​ NLP&#xff08;Natural Language Processing&#xff09;&#xff0…

Word2Vec基本实践

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

西木科技Westwood-Robotics人型机器人Bruce配置和真机配置

西木科技Westwood-Robotics人型机器人Bruce配置和真机配置 本文内容机器人介绍Bruce机器人Gazebo中仿真代码部署Bruce真机代码部署 本文内容 人形机器人Brcue相关介绍docker中安装Gazebo并使用Bruce机器人控制器更换环境配置 机器人介绍 公司&#xff1a;西木科技Westwood-R…

企业UDP文件传输工具测速的方式(下)

在前一篇文章中&#xff0c;我们深入讨论了UDP传输的基本概念和镭速UDP文件传输工具如何使用命令行快速进行速度测试。现在&#xff0c;让我们进一步探索更为高级和灵活的方法&#xff0c;即通过整合镭速UDP的动态或静态库来实现网络速度的测量&#xff0c;以及如何利用这一过程…

CVPR24最佳论文 | 谷歌:生成图像动力学

原文&#xff1a;Generative Image Dynamics 地址&#xff1a;https://generative-dynamics.github.io/ 代码&#xff1a;未知 出版&#xff1a;CVPR 2024 机构: 谷歌 1 研究问题 本文研究的核心问题是: 如何从单张静止图片生成逼真的长时间动态视频&#xff0c;同时支持用…

Socket 原理和思考

众所周知Reactor是一种非常重要和应用广泛的网络编程模式&#xff0c;而Java NIO是Reactor模式的一个具体实现&#xff0c;在Netty和Redis都有对其的运用。而不管上层模式如何&#xff0c;底层都是走的Socket&#xff0c;对底层原理的了解会反哺于上层&#xff0c;避免空中楼阁…

【数学建模】解析几何与方程模型

文章目录 解析几何与方程模型1.几何建模思想2.Numpy在线性代数中的使用3.国赛求解3.1题目3.2 问题1求解建立模型代码求解 3.3 问题2求解 4.问题答疑Q1:什么是行列式&#xff0c;其使用场景是什么行列式的定义行列式的性质行列式的使用场景 Q2:2023B题问题一用相似三角形求解覆盖…

htb_Editorial

hack the book Editorial 端口扫描 80 22 目录扫描 /upload 是一个上传book information的页面 其中最顶上有一个可以上传书本封面的地方&#xff0c;可以从本地上传&#xff0c;也可以从远程下载 这里可能涉及ssrf和本地文件上传&#xff0c;逐一尝试 随便上传一个图片…