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

时间:2016-01-22 04:51:49

标签: xcode swift macos smjobbless

我正在写一些需要经常以root权限运行命令的软件。

现在,我这样做是通过向用户询问他们的密码一次,保存密码,然后将该密码作为参数提供给NSAppleScript以及with administrator privileges

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

我一直在寻找更好的一周时间,但找不到解决方案。

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

我已经关注了app的示例,我从他们的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

<?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>

Apps Info 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和其他很多人喜欢它。据我所知,我的钳子设置正确。我做错了什么?

2 个答案:

答案 0 :(得分:6)

此方法的关键部分在ReadMe.txt的“工作原理”下的“财产列表”部分中描述:

  

[...]当您使用开发者ID对帮助工具进行签名时,Xcode会自动设置帮助工具的指定要求,这就是您应该为SMPrivilegedExecutables使用的内容。此外,这就是上面显示的“setreq”命令:从构建的工具中提取指定的需求并将其放入应用程序的Info.plist源代码中。

由于您未签署产品(至少不使用示例中描述的证书),此过程将始终失败。

如果您不在开发者计划中,可以create a self-signed certificate进行签名。然而,这或多或少地违背了签署要求的目的。如果您不打算注册开发人员计划,您应该能够按如下方式缩短该流程:

  1. 在您的应用的Info.plist中,缩写SMPrivilegedExecutables下的要求以匹配帮助者的标识符:

    <string>identifier "com.domain.AppName.SampleService"</string>

    • 在帮助者的Info.plist中,将SMAuthorizedClients下的要求缩写为仅匹配应用的标识符:

    <string>identifier "com.domain.AppName"</string>

    • 忽略ReadMe.txt的“构建并运行示例”说明,并且只需正常构建和运行项目。
  2. 我不能说我当然推荐这个;这些签署要求存在是有充分理由的。它至少比最终替代方案更好,然而,它将使用NSAppleScript通过chmodchown向帮助程序执行根setuid位。

    附录详细阐述了这里的一些概念:

    运行特权代码会带来许多潜在的安全漏洞;安全地验证用户只是第一步。将所有特权操作委派给单独的进程是另一个重要步骤,但仍然存在的主要问题是如何确保您的应用程序 - 用户实际授予的权限 - 是唯一能够利用特权访问权限的实体。

    Apple的示例演示了使用代码签名来解决此问题。以防您不熟悉:代码签名涉及以加密方式标记您的最终产品,以便OS X可以验证您的程序是否未被受损版本替换。那些额外的“证书叶子”引用在原始示例SMAuthorizedClients中,SMPrivilegedExecutables专门用于此;他们描述了您的应用和帮助者必须签署的证书,以便彼此互动。

    为了帮助描绘一下图片,这里有一个粗略的概述:

    1. 您的用户授予launchd授权以安装标有com.domain.AppName.SampleService的帮助程序守护程序。
    2. launchd在您的应用的Info.plist中找到com.domain.AppName.SampleService下的SMPrivilegedExecutables条目;这描述了应该使用帮助程序的二进制文件签署的证书。 (如果它们不匹配,那么理论上攻击者已经用自己的版本替换了你的帮助工具,以便以root身份运行它。)
    3. 安装了有效的帮助工具后,您的应用会发出启动请求以在您的控制下生成帮助程序。此时,launchd会查询帮助工具的Info.plist中的SMAuthorizedClients部分,以确保应用程序确实有权运行该工具。当然,它会验证您应用的签名,以确保它没有被篡改。
    4. 回到您的方案,您的产品目前的工作方式是取消签名步骤。你指示启动检查的唯一一件事是你的应用程序的Info.plist是否将其ID列为“com.domain.AppName”。由于没有什么可以阻止攻击者更改他们的Info.plist来说明这一点,所以你希望他们一旦掌握了它就无法使用你的助手工具做任何伤害。

      其他附录概述了替代方案:

答案 1 :(得分:0)

你正朝着正确的方向前进。当前特权帮助工具是在特权模式下执行任务的最佳实践。为此,您也可以使用Swift,但只需用Swift替换C版函数调用。 (Apple在10.11 SDK中引入了替代品)例如,而不是

Boolean SMJobBless( CFStringRef domain, CFStringRef executableLabel, AuthorizationRef auth, CFErrorRef *outError);

你可以使用:

SMJobBless(_: CFString!, _: CFString, _: AuthorizationRef, _: UnsafeMutablePointer<Unmanaged<CFError>?>) -> UInt8

但我从未见过Internet中特权助手工具的例子......所以你需要看看Objective C代码。幸运的是Obj C代码并不多。