草庐IT

xcode - 使用 swift 为 Mac 应用程序获得管理权限

coder 2023-09-05 原文

我正在编写一个需要经常使用 root 权限运行命令的软件。

现在,我通过向用户询问密码一次,保存密码,然后将该密码提供给 NSAppleScript 来执行此操作。与 with administrator privileges 一起作为参数.

这对用户来说显然是不安全的,因为有人可以访问他们的密码。

我一直在寻找一周的大部分时间,但找不到解决方案。

SMJobBless似乎允许您以更高的权限安装应用程序。

我遵循了应用程序的示例,但从他们的 SMJobBlessUtil 脚本中收到错误消息。

这是错误:

SMJobBlessUtil.py: tool designated requirement (identifier "com.domain.AppName.SampleService" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: firstName lastName (XXXXXXXXXX)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */) doesn't match entry in 'SMPrivilegedExecutables' (anchor apple generic and identifier "com.domain.AppName.SampleService" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.CN] = "Mac Developer: firstName lastName (XXXXXXXXXX)")

显然,有什么地方不对劲。这是相应的 plist

服务信息 plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleIdentifier</key>
    <string>com.domain.AppName.SampleService</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>SampleService</string>
    <key>CFBundleVersion</key>
    <string>6</string>
    <key>SMAuthorizedClients</key>
    <array>
        <string>anchor apple generic and identifier "com.domain.AppName" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = xxxxxxxxxx)</string>
    </array>
</dict>
</plist>

应用信息 plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleDisplayName</key>
    <dict/>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleGetInfoString</key>
    <dict/>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>Away</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0.99</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>9</string>
    <key>LSApplicationCategoryType</key>
    <string>public.app-category.utilities</string>
    <key>LSMinimumSystemVersion</key>
    <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
    <key>LSUIElement</key>
    <true/>
    <key>NSHumanReadableCopyright</key>
    <string>Copyright © 2016 firstName lastName. All rights reserved.</string>
    <key>NSMainStoryboardFile</key>
    <string>Main</string>
    <key>NSPrincipalClass</key>
    <string>NSApplication</string>
    <key>SMPrivilegedExecutables</key>
    <dict>
        <key>com.domain.AppName.SampleService</key>
        <string>anchor apple generic and identifier "com.domain.AppName.SampleService" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.CN] = "Mac Developer: firstName lastName (XXXXXXXXXX)"</string>
    </dict>
</dict>
</plist>

我看过 at this stackoverflow post还有很多人喜欢它。据我了解,我的 plist 设置正确。我究竟做错了什么?

最佳答案

ReadMe.txt 的“How It Works”下的“PROPERTY LISTS”部分描述了这种方法的关键部分:

[…] when you sign the helper tool with a Developer ID, Xcode automatically sets the helper tool's designated requirement like this, and that's what you should use for SMPrivilegedExecutables. Moreover, this is what the "setreq" command shown above does: extracts the designated requirement from the built tool and put it into the app's Info.plist source code.


由于您没有签署产品(至少没有使用示例中描述的证书),因此此过程将始终失败。
如果您不在开发人员计划中,您可以 create a self-signed certificate签署。然而,这或多或少违背了签署要求的目的。如果您不打算注册开发人员计划,您应该能够将流程简化如下:
  • 在您的应用程序的 Info.plist 中,缩写 SMPrivilegedExecutables 下的要求只匹配助手的标识符:
  • <string>identifier "com.domain.AppName.SampleService"</string>
  • 在您的助手的 Info.plist 中,缩写 SMAuthorizedClients 下的要求只匹配应用程序的标识符:
  • <string>identifier "com.domain.AppName"</string>
  • 忽略 ReadMe.txt 中的“构建和运行示例”说明,而只是照常构建和运行项目。

  • 我当然不能说我推荐这个;这些签署要求的存在是有充分理由的。然而,它至少比最终的替代方案要好,后者将使用 NSAppleScript 通过 chmod 为辅助可执行文件提供一个 root setuid 位。和 chown .

    附录详细阐述了这里的一些概念:
    运行特权代码会带来很多潜在的安全漏洞;安全地验证用户只是第一步。将所有特权操作委托(delegate)给一个单独的进程是另一个强有力的步骤,但仍然存在的主要问题是如何确保您的应用程序——用户实际授予权限的应用程序——是唯一能够利用特权访问的实体。
    Apple 的示例演示了使用代码签名来解决此问题。以防万一您不熟悉:代码签名涉及以加密方式标记您的最终产品,以便 OS X 可以验证您的程序没有被损坏的版本替换。原始示例 SMAuthorizedClients 中那些额外的“证书叶”引用和 SMPrivilegedExecutables专门为此;它们描述了您的应用程序和助手必须使用的证书才能相互交互。 除了在交互期间验证代码签名之外,还记录了用于其他目的。正如 this blog post and the corresponding CVE 所指出的,客户端程序员负责确保帮助工具和应用程序之间的加密安全进程间通信,例如使用 SecCode应用程序接口(interface)。
    为了帮助描绘一下,这里有一个关于它是如何发挥作用的粗略概述:
  • 您的用户授权 launchd 安装标记为 com.domain.AppName.SampleService 的辅助守护程序.
  • launchd 定位到 com.domain.AppName.SampleService条目下 SMPrivilegedExecutables在您的应用程序的 Info.plist 中;这描述了帮助程序的二进制文件应该使用的证书。 (如果它们不匹配,那么理论上攻击者已经用他们自己的版本替换了您的辅助工具,以便以 root 身份运行它。)请注意 documentation具体说明 key SMPrivilegedExecutables仅与更新目的相关:

  • The calling application's Info.plist must include a "SMPrivilegedExecutables" dictionary of strings. Each string is a textual representation of a code signing requirement used to determine whether the application owns the privileged tool once installed (i.e. in order for subsequent versions to update the installed version). 3. With the valid helper tool installed, your app makes a request to launchd to spawn the helper under your control. At this point, launchd consults the SMAuthorizedClients section of your helper tool's Info.plist to ensure the app actually does have the right to run the tool. And, of course, it verifies your app's signature to ensure it hasn't been tampered with. Note, again, that the documentation specifically describes the key SMAuthorizedClients to be only relevant for updating purposes: The helper tool must have an embedded Info.plist containing an "SMAuthorizedClients" array of strings. Each string is a textual representation of a code signing requirement describing a client which is allowed to add and remove the tool.


    回到您的场景,您的产品当前的工作方式是消除签名步骤。您指示 launchd 检查的唯一一件事是您的应用程序的 Info.plist 是否将其 ID 列为“com.domain.AppName”。由于没有什么可以阻止攻击者更改他们的 Info.plist 来说明这一点,因此您寄希望于他们一旦控制了您的辅助工具就无法使用您的辅助工具造成任何伤害。
    概述替代方案的附加附录:
  • 正如我之前提到的,可以使用您在 Keychain Access 中自己创建的证书对您的代码进行签名。但是,由于该证书不会在权威机构(即 Apple)注册,因此被欺骗为 com.domain.AppName 并不困难。将。
  • 实现您自己的加密解决方案,作为您的应用程序和助手之间通信的一部分。例如,您可以 generate a pair of keys during installation of the helper, store them via Keychain Services so that your programs have access to them, and verify them against one another when using the helper in the future.
  • 报名参加 Apple Developer program为了按照 Apple 的意图签署您的代码;这为您提供了 OS X 的额外好处,而不是用“身份不明的开发人员”schtick 吓跑您的用户。
  • 关于xcode - 使用 swift 为 Mac 应用程序获得管理权限,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34939243/

    有关xcode - 使用 swift 为 Mac 应用程序获得管理权限的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    5. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    7. ruby - i18n Assets 管理/翻译 UI - 2

      我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

    8. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

      我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

    9. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

      关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

    10. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    随机推荐