【问题标题】:Keychain Access as root user from MAC Application从 MAC 应用程序以 root 用户身份访问钥匙串
【发布时间】:2015-03-31 09:18:53
【问题描述】:

我正在使用 MAC 应用程序从我的应用程序创建 VPN 连接。

经过大量研究,我发现我需要以 ROOT 身​​份运行应用程序以将用户密码和 sharedSecretKey 存储在 SYSTEM 钥匙串中。

应用程序用户不会以 ROOT 身​​份打开应用程序,因此我需要在 SYSTEM KEYCHAIN 中添加用户密码和 sharedsecretkey 而无需 ROOT 访问权限。

我在网上搜索,发现 Apple 提供此代码:https://developer.apple.com/library/mac/samplecode/SMJobBless/Introduction/Intro.html https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html

但我不明白如何在我的应用程序中使用这 2 个代码将用户的密码和 SharedSecretKey 存储在 SYSTEM KEYCHAIN 中,而无需 root 访问权限

我们将不胜感激。

提前致谢。

这是我在 SYSTEM KEYCHAIN 中添加密码的代码,如果我以 ROOT 身​​份运行我的代码,这将非常有用。

// Vendor dependencies
#import <Security/SecKeychain.h>

// Local dependencies
#import "VPNKeychain.h"

// These are the applications which are going to get access to new Keychain items.
// How do we know them? Just create a VPN service manualy and run the following command:
//   security dump-keychain -a /Library/Keychains/System.keychain
// Among the results, you will find your VPN service and you can see the paths that have access to it
static const char * trustedAppPaths[] = {
      "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/Helpers/SCHelper",          "/System/Library/PreferencePanes/Network.prefPane/Contents/XPCServices/com.apple.preference.network.remoteservice.xpc",
  "/System/Library/CoreServices/SystemUIServer.app",
  "/usr/sbin/pppd",
  "/usr/sbin/racoon",
  "/usr/libexec/configd",
    };

// This class contains all code we need to handle System Keychain Items
// Exit status codes: 60-79
@implementation VPNKeychain


// This will create a PPP Password Keychain Item
+ (int) createPasswordKeyChainItem:(NSString*)label forService:(NSString*)service withAccount:(NSString*)account andPassword:(NSString*)password {
  return [self createItem:label withService:service account:account description:@"PPP Password" andPassword:password];
}

// This will create an IPSec Shared Secret Keychain Item
+ (int) createSharedSecretKeyChainItem:(NSString*)label forService:(NSString*)service withPassword:(NSString*)password {
  service = [NSString stringWithFormat:@"%@.SS", service];
  return [self createItem:label withService:service account:@"" description:@"IPSec Shared Secret" andPassword:password];
}


// A generic method to create Keychain Items holding Network service passwords
+ (int) createItem:(NSString*)label withService:(NSString*)service account:(NSString*)account description:(NSString*)description andPassword:(NSString*)password {

  // This variable will hold all sorts of operation status responses
  OSStatus status;

  // Converting the NSStrings to char* variables which we will need later
  const char *labelUTF8 = [label UTF8String];
  const char *serviceUTF8 = [service UTF8String];
  const char *accountUTF8 = [account UTF8String];
  const char *descriptionUTF8 = [description UTF8String];
  const char *passwordUTF8 = [password UTF8String];

  // This variable is soon to hold the System Keychain
  SecKeychainRef keychain = NULL;

  status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem,     &keychain);
  if (status == errSecSuccess) {
    NSLog(@"Succeeded opening System Keychain");
  } else {
    NSLog(@"Could not obtain System Keychain: %@", SecCopyErrorMessageString(status, NULL));
    return 60;
  }

  NSLog(@"Unlocking System Keychain");
  status = SecKeychainUnlock(keychain, 0, NULL, FALSE);
  if (status == errSecSuccess) {
    NSLog(@"Succeeded unlocking System Keychain");
  } else {
    NSLog(@"Could not unlock System Keychain: %@", SecCopyErrorMessageString(status, NULL));
    return 61;
  }

  // This variable is going to hold our new Keychain Item
  SecKeychainItemRef item = nil;

    SecAccessRef access = nil;
  status = SecAccessCreate(CFSTR("Some VPN Test"), (__bridge CFArrayRef)(self.trustedApps), &access);

  if(status == noErr) {
    NSLog(@"Created empty Keychain access object");
  } else {
    NSLog(@"Could not unlock System Keychain: %@", SecCopyErrorMessageString(status, NULL));
    return 62;
  }

  // Putting together the configuration options
  SecKeychainAttribute attrs[] = {
{kSecLabelItemAttr, (int)strlen(labelUTF8), (char *)labelUTF8},
{kSecAccountItemAttr, (int)strlen(accountUTF8), (char *)accountUTF8},
{kSecServiceItemAttr, (int)strlen(serviceUTF8), (char *)serviceUTF8},
{kSecDescriptionItemAttr, (int)strlen(descriptionUTF8), (char *)descriptionUTF8},
  };

  SecKeychainAttributeList attributes = {sizeof(attrs) / sizeof(attrs[0]), attrs};

  status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, (int)strlen(passwordUTF8), passwordUTF8, keychain, access, &item);

  if(status == noErr) {
    NSLog(@"Successfully created Keychain Item");
  } else {
    NSLog(@"Creating Keychain item failed: %@", SecCopyErrorMessageString(status, NULL));
    return 63;
  }
  return 0;
}

+(NSArray*) trustedApps {
  NSMutableArray *apps = [NSMutableArray array];
  SecTrustedApplicationRef app;
  OSStatus err;

  for (int i = 0; i < (sizeof(trustedAppPaths) /     sizeof(*trustedAppPaths)); i++) {
    err = SecTrustedApplicationCreateFromPath(trustedAppPaths[i], &app);
    if (err == errSecSuccess) {
      //NSLog(@"SecTrustedApplicationCreateFromPath succeeded: %@", SecCopyErrorMessageString(err, NULL));
    } else {
      NSLog(@"SecTrustedApplicationCreateFromPath failed: %@", SecCopyErrorMessageString(err, NULL));
    }

    [apps addObject:(__bridge id)app];
  }

  return apps;
}

【问题讨论】:

    标签: macos osx-yosemite vpn keychain system-configuration


    【解决方案1】:

    在 OS X 中,应用程序不直接处理用户的凭据,而是通过函数调用 AuthorizationCopyRights 请求系统执行此操作,该函数记录在 Authorization Services 中。

    Gui 应用程序不能直接执行管理(root)操作,并且由于 Yosemite (10.10),Gui 应用程序不能以 root 身份运行。相反,您的应用程序必须通过 XPC 服务使用“帮助”应用程序,这就是 SMJobBless 和 BetterAuthorization 示例所展示的。你可以阅读更多关于XPC here的信息。

    在您的情况下,您需要创建这样一个帮助应用程序,它具有访问系统钥匙串的必要权限。

    请注意,如果您计划通过 Apple Store 分发您的应用程序,则该应用程序必须经过沙盒处理并且不能使用任何安全服务,例如调用函数 AuthorizationCopyRights。

    【讨论】:

    • okey.. 根据您的回答,我正在错误的方向搜索以将我的代码作为路线运行。我尝试了苹果的代码“EvenBetterAuthorizationSample”来实现帮助工具,但我没有找到一种合适的方法来为我现有的应用程序实现代码。你能帮我从那个代码中实现 HELPER
    • 请更新您的问题,解释您使用 EvenBetterAuthorizationSample 尝试过的内容以及不适合您的内容。
    • 好吧,我从该示例中读取了“readme.txt”文件,但我不明白如何在我的代码中使用该示例将密码存储在系统钥匙串中。你能指导我如何将来自 evenabetter 示例的帮助器用于我的代码
    • 首先让助手工作。助手应用程序使用钥匙串服务通过函数 SecKeychainAddGenericPassword 在系统钥匙串中添加密码:developer.apple.com/library/mac/documentation/Security/…
    • 我有在 SYSTEM KEYCHAIN 中添加密码的代码,当我以 root 身份运行我的代码时它工作正常。但是我怎么能在那个中使用“EvenBetterAuthorizationSample”代码......
    【解决方案2】:

    您链接到的示例代码中对此进行了说明,请参阅ReadMe.txt

    运行示例后系统会提示您输入管理员用户名和 密码。输入您的管理员用户名和密码,如果一切顺利 好吧,示例的窗口将显示“帮助工具可用!” 表示一切正常。如果没有,您可以在控制台中查看 记录有关失败的信息。

    因此,通常情况下,您的应用程序必须在某个时候要求提供管理员凭据。

    更新:

    这应该通过特权帮助工具来完成,如引用的SMJobBless 示例所示。您的帮助工具应该为您的应用程序执行钥匙串访问。以下是安装此类帮助工具的主要步骤:

    • 使用AuthorizationCreate函数创建授权对象。

    • 使用给定的权限集对对象执行预授权 AuthorizationCopyRights 函数。这实际上会导致要求您的用户提供管理员凭据。

    • 使用 launchd 验证、安装和注册帮助工具 SMJobBless 函数。

    安装并注册帮助工具后,您应该使用NSXPCConnection 与您的帮助工具对话。有关如何实现它的详细信息,请参阅Sandboxing with NSXPCConnection 示例代码。

    【讨论】:

    • 是的,我知道,但是你能指导我下载后在哪里需要修改代码,因为我有开发者 ID 和证书,所以我想使用我的证书和开发者 ID。
    • 或者我如何将此代码集成到我的 xCode 的 MAC 应用程序项目中。
    • 用户的应用从不要求用户提供凭据。提示来自系统通过安全守护进程和安全代理。
    • 当然,我的意思是他不必像问题描述的那样以 root 用户运行他的应用程序。 SMJobBless 将负责提权
    • 如果这就是你的意思,我建议你更新你的答案以反映这一点。
    猜你喜欢
    • 1970-01-01
    • 2019-09-06
    • 2016-09-14
    • 2020-06-27
    • 2018-05-14
    • 2014-08-12
    • 2020-02-25
    • 1970-01-01
    • 2012-06-29
    相关资源
    最近更新 更多