Keychain Access as root user from MAC Application

5.8k Views Asked by At

I am working on MAC Application to create VPN Connection from MY Application.

After a lot of Research i found that i need to run application as ROOT to store User password and sharedSecretKey in SYSTEM keychain.

Application user will not gonna open application as ROOT so that i need to add user password and sharedsecretkey in SYSTEM KEYCHAIN without ROOT Access.

I search on web on this and found that Apple Provide this code : https://developer.apple.com/library/mac/samplecode/SMJobBless/Introduction/Intro.html https://developer.apple.com/library/mac/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html

but didn't understand how can i use this 2 code in my application to store user's password and SharedSecretKey in SYSTEM KEYCHAIN WITH OUT ROOT ACCESS.

Any help will be appreciated.

Thanks in advance.

Here is my code to add Password in SYSTEM KEYCHAIN Which is work great if i run my code as 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;
}
2

There are 2 best solutions below

6
On

It is explained in the sample code that you link to, see ReadMe.txt:

Once you run the sample you'll be prompted for an admin user name and password. Enter your admin user name and password and, if all goes well, the sample's window will show "The Helper Tool is available!" indicating that everything is OK. If not, you can look in the console log for information about the failure.

So generally, your application will have to ask for admin credentials at some point.

Update:

This should be done through a privileged helper tool, as demonstrated in cited SMJobBless example. Your helper tool should perform keychain access for your app. Here are main steps to install such helper tool:

  • Create authorisation object with AuthorizationCreate function.

  • Perform preauthorisation on the object with given set of rights using AuthorizationCopyRights function. This will in fact result in asking your user for admin credentials.

  • Verify, install and register helper tool with launchd using SMJobBless function.

Once the helper tool is installed and registered you should use NSXPCConnection to talk to your helper tool. See Sandboxing with NSXPCConnection sample code for details on how to achieve it.

7
On

In OS X, applications do not handle users' credentials directly, but instead request the system to do so, via the function call AuthorizationCopyRights, which is documented in Authorization Services.

A Gui application cannot directly perform administrative (root) actions and since Yosemite (10.10), Gui applications cannot run as root. Instead, your application must use a 'helper' application via XPC services, which is what the SMJobBless and BetterAuthorization samples demonstrate. You can read more about XPC here.

In your case, you would need to create such a helper application, which will have the necessary rights to access the system keychain.

Note that if you plan to distribute your application via the Apple Store, the application must be sandboxed and cannot use any security services, such as calling the function AuthorizationCopyRights.