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

Cri*_*pto 5 macos xcode swift smjobbless

我正在写一些需要经常运行具有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)")
Run Code Online (Sandbox Code Playgroud)

显然,出了点问题.这是各自的plists

服务信息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>
Run Code Online (Sandbox Code Playgroud)

应用信息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>
Run Code Online (Sandbox Code Playgroud)

我看过这个stackoverflow帖子和许多其他人喜欢它.据我所知,我的钳子设置正确.我究竟做错了什么?

Sam*_* R. 6

这种方法的关键部分在ReadMe.txt的"如何工作"下的"PROPERTY LISTS"部分中描述:

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

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

如果您不在开发人员计划中,则可以创建自签名证书以进行签名.然而,这或多或少地违背了签署要求的目的.如果您不打算注册开发人员计划,您应该能够按如下方式缩短该流程:

  1. 在您的应用程序的Info.plist中,将需求缩写SMPrivilegedExecutables为仅匹配帮助程序的标识符:

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

    • 在您的帮助器的Info.plist中,将需求缩写SMAuthorizedClients为仅匹配应用程序的标识符:

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

    • 忽略ReadMe.txt的"构建和运行示例"指令,并简单地构建和运行项目.

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


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

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

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

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

  1. 您的用户授予launchd授权以安装标记的帮助程序守护程序com.domain.AppName.SampleService.
  2. launchd 在你的应用程序的Info.plist中找到该com.domain.AppName.SampleService条目SMPrivilegedExecutables; 这描述了应该使用帮助程序的二进制文件签署的证书.(如果它们不匹配,那么理论上攻击者已经用自己的版本替换了你的帮助工具,以便以root身份运行它.)
  3. 安装了有效的帮助工具后,您的应用程序会发出启动请求以在您的控制下生成帮助程序.此时,launchd会查询SMAuthorizedClients帮助工具的Info.plist部分,以确保应用程序实际上有权运行该工具.当然,它会验证您应用的签名,以确保它没有被篡改.

回到您的方案,您的产品目前的工作方式是取消签名步骤.你指示启动检查的唯一一件事是你的应用程序的Info.plist是否将其ID列为"com.domain.AppName".由于没有什么可以阻止攻击者改变他们的Info.plist来说明这一点,所以你希望他们无法使用你的帮助工具在他们控制它之后做任何伤害.

其他附录概述了备选方案: