全球活动、Mac App Store 和沙盒
Posted
技术标签:
【中文标题】全球活动、Mac App Store 和沙盒【英文标题】:Global events, the Mac App Store, and the sandbox 【发布时间】:2012-09-14 07:30:33 【问题描述】:我正在开发一个应用程序,其中使用全局按键事件将是其操作的要求。此外,我计划通过 App Store 严格分发它。 (这是一个 Mac 应用程序,而不是 ios。)我得到了一个通过 addGlobalMonitorForEventsMatchingMask 监听全局事件的示例,但有一些警告。
注意:我选择使用现代 API,而不是依赖早期的 Carbon 热键方法。如果它们最终被弃用,我不想稍后再解决这个问题。
主要问题是必须信任应用程序才能检测到全局事件。否则,必须为所有应用程序启用可访问性。当我启用可访问性时,会成功检测到事件。此要求记录在此处,https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/MonitoringEvents/MonitoringEvents.html。
我希望我的用户不必启用可访问性。根据我所做的其他研究,您可以通过调用 AXMakeProcessTrusted,然后重新启动应用程序来获得信任的应用程序。
在我使用的代码中,我没有收到身份验证提示。该应用程序将重新启动,但仍然不受信任(可能是因为我没有收到身份验证提示)。这是我这部分的代码:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
if (!AXAPIEnabled() && !AXIsProcessTrusted())
NSString *appPath = [[NSBundle mainBundle] bundlePath];
AXError error = AXMakeProcessTrusted( (CFStringRef)CFBridgingRetain(appPath) );
[self restartApp];
- (void)restartApp
NSTask *task = [[NSTask alloc] init];
NSMutableArray *args = [NSMutableArray array];
[args addObject:@"-c"];
[args addObject:[NSString stringWithFormat:@"sleep %d; open \"%@\"", 3, [[NSBundle mainBundle] bundlePath]]];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:args];
[task launch];
[NSApp terminate:nil];
此外,我在https://developer.apple.com/library/archive/documentation/Security/Conceptual/authorization_concepts/03authtasks/authtasks.html#//apple_ref/doc/uid/TP30000995-CH206-BCIGAIAG 处查看了授权服务任务的文档。
首先让我担心的是这个信息框,“重要的是,应用沙箱中不支持授权服务 API,因为它允许权限提升。”
如果需要此 API 在重新启动应用程序之前获取身份验证提示,我可能无法在不启用辅助功能的情况下获取全局事件。
总之,我的具体问题是:
我的示例代码中关于如何获取 出现身份验证提示?
为了让身份验证提示出现,我是否需要 使用授权服务 API?
是否有可能拥有一个沙盒应用程序 访问全球活动?
【问题讨论】:
你设法解决这个问题了吗?我偶然发现了同样的问题:( 不是直接的。出于全局热键的目的,我不得不走使用 Cocoa 热键的路线。 我也在寻找提示用户的方法。我的猜测是,在安装应用程序并调用 AXMakeProcessTrusted 时,安装程序将以具有必要权限的 root 身份运行。如果安装程序是 Mac App Store,则必须有一个钩子来请求此权限。 【参考方案1】:首先,您无法自动允许应用使用可在沙盒环境和应用商店中工作的无障碍 API。推荐的方法是简单地引导用户,以便他们可以轻松地自己启用它。新的 API 调用 AXIsProcessTrustedWithOptions
正是为此:
NSDictionary *options = @(id) kAXTrustedCheckOptionPrompt : @YES;
AXIsProcessTrustedWithOptions((CFDictionaryRef) options);
现在,对于您的第一个和第二个问题(只是为了完整起见 - 它在沙盒中也不起作用):
AXMakeProcessTrusted
背后的想法是,您实际上创建了一个新的 auxiliary application,您可以从主应用程序以 root 身份运行它。然后,此实用程序调用AXMakeProcessTrusted
,传入主应用程序的可执行文件。最后,您必须重新启动主应用程序。在 OSX 10.9 中已弃用 API 调用。
要以 root 身份生成新进程,您必须使用 launchd
和 SMJobSubmit
。这将通过身份验证提示提示用户,说明应用程序正在尝试安装帮助工具以及是否应允许。具体来说:
+ (BOOL)makeTrustedWithError:(NSError **)error
NSString *label = FMTStr(@"%@.%@", kShiftItAppBundleId, @"mktrusted");
NSString *command = [[NSBundle mainBundle] pathForAuxiliaryExecutable:@"mktrusted"];
AuthorizationItem authItem = kSMRightModifySystemDaemons, 0, NULL, 0;
AuthorizationRights authRights = 1, &authItem;
AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
AuthorizationRef auth;
if (AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &auth) == errAuthorizationSuccess)
// this is actually important - if from any reason the job was not removed, it won't relaunch
// to check for the running jobs use: sudo launchctl list
// the sudo is important since this job runs under root
SMJobRemove(kSMDomainSystemLaunchd, (CFStringRef) label, auth, false, NULL);
// this is actually the launchd plist for a new process
// https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man5/launchd.plist.5.html#//apple_ref/doc/man/5/launchd.plist
NSDictionary *plist = @
@"Label" : label,
@"RunAtLoad" : @YES,
@"ProgramArguments" : @[command],
@"Debug" : @YES
;
BOOL ret;
if (SMJobSubmit(kSMDomainSystemLaunchd, (CFDictionaryRef) plist, auth, (CFErrorRef *) error))
FMTLogDebug(@"Executed %@", command);
ret = YES;
else
FMTLogError(@"Failed to execute %@ as priviledged process: %@", command, *error);
ret = NO;
// From whatever reason this did not work very well
// seems like it removed the job before it was executed
// SMJobRemove(kSMDomainSystemLaunchd, (CFStringRef) label, auth, false, NULL);
AuthorizationFree(auth, 0);
return ret;
else
FMTLogError(@"Unable to create authorization object");
return NO;
至于重启,这通常也使用外部实用程序来完成,该实用程序等待主应用程序完成并再次启动它(通过使用 PID)。如果您使用sparkle framework,您可以重复使用现有的:
+ (void) relaunch
NSString *relaunch = [[NSBundle bundleForClass:[SUUpdater class]] pathForResource:@"relaunch" ofType:@""];
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *pid = FMTStr(@"%d", [[NSProcessInfo processInfo] processIdentifier]);
[NSTask launchedTaskWithLaunchPath:relaunch arguments:@[path, pid]];
[NSApp terminate:self];
另一种选择是破解/Library/Application Support/com.apple.TCC/TCC.db
sqlite 数据库,使用辅助助手手动添加权限:
NSString *sqlite = @"/usr/bin/sqlite3";
NSString *sql = FMTStr(@"INSERT or REPLACE INTO access values ('kTCCServiceAccessibility', '%@', 1, 1, 1, NULL);", MY_BUNDLE_ID);
NSArray *args = @[@"/Library/Application Support/com.apple.TCC/TCC.db", sql];
NSTask *task = [NSTask launchedTaskWithLaunchPath:sqlite arguments:args];
[task waitUntilExit];
但是,这将取消该应用成为应用商店的资格。更重要的是,它实际上只是一个 hack,并且 db / schema 可以随时更改。一些应用程序(例如,曾经这样做的 Divvy.app)在应用程序安装程序安装后脚本中使用了这个 hack。这样可以防止对话框告诉应用程序正在请求安装辅助工具。
【讨论】:
选项 2(直接更改用户的隐私信任数据库)几乎肯定会取消他的应用程序在 Mac App Store 中的资格,因为它会构成使用私有 API、修改系统文件并破坏应用程序沙箱. Apple 还表示,如果人们开始发布依赖它的应用程序,他们将消除这种方法的可行性。 @Draxillion - 你介意提供苹果声明的链接吗? 这是向一位长期的 Mac 开发者私下声明的——它在 Apple 开发者论坛上被引用,但我没有直接链接。 好吧,它终于发生了:10.12 GM 现在通过“restricted”扩展属性将 TCC.db 置于系统完整性保护 (SIP) 控制之下。这可以防止所有非 Apple 签名的应用程序修改 TCC.db。看起来 Dropbox 可能是最终迫使 Apple 出手的开发商。【参考方案2】:基本上,MAS 限制将要求您让 tge 用户为所有人打开 AX。
【讨论】:
【参考方案3】:我在 GitHub 上找到了一个潜在的解决方案。
https://github.com/K8TIY/CW-Station
它有一个辅助应用程序,它将在根目录下运行以请求对主应用程序的访问。它有点过时并且正在使用一些已被弃用的功能,所以我正在努力对其进行现代化改造。这看起来是一个很好的起点。
【讨论】:
以上是关于全球活动、Mac App Store 和沙盒的主要内容,如果未能解决你的问题,请参考以下文章