Keychain Access as root user from MAC Application - macos

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;
}

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.

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.

Related

Authorization by AEDeterminePermissionToAutomateTarget waits infinit time

I use this method to check Apple Event (Automation) permission:
bool checkSIPforAppIdentifier(const QString &appId)
{
OSStatus status = noErr;
if (#available(macOS 10.14, *)) {
NSAppleEventDescriptor *targetAppEventDescriptor;
targetAppEventDescriptor = [NSAppleEventDescriptor descriptorWithBundleIdentifier:appId.toNSString()];
status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true);
}
return status == noErr;
}
The problem is that the execution freezes at API: AEDeterminePermissionToAutomateTarget and the user is not prompted for authorization.
usage example:
checkSIPforAppIdentifier("com.microsoft.Word");
I have inserted necessary key in info.plist:
<key>NSAppleEventsUsageDescription</key>
<string>XXX uses this feature to do do Typography actions.</string>
My App is not sandboxed.
Hardened Runtime open Apple Events
enter image description here

Accessing KeyChain item from OSX Authorization Plugin

I am trying to access Keychain Item after my FileVault login, But I am unable to do this.I have seen that similar kind of issue was in Error acessing keychain item from OS X authorization plugin ,Please help me with this already struggling with this from a week.
I've encountered a similar situation, it appears that non privileged authorization plugins cannot access 'system' keychain. So I created in advance a new keychain with the items required by the authentication plugin.
// blabla is pw for keychain and blabla2 is pw for p12 file.
security create-keychain -p balboa /tmp/my.keychain
security unlock-keychain -p balboa /tmp/my.keychain
sudo -A security import /tpm/mycert.p12 -k /tmp/my.keychain -P blabla2 -A
Than, From the authorization plugin, I successfully manage to access the item inside the keychain. In the following pseudo code, I demonstrate how to get a certificate from keychain according to issuer, but any other way for accessing the keychain for reading is also allowed.
SecKeychainRef myKeychain = nil;
SecKeychainOpen( "/tmp/my.keychain", &myKeychain);
NSString * keychainPassword = #"blabla";
SecKeychainUnlock(myKeychain, [keychainPassword length], [keychainPassword UTF8String], true);
CFArrayRef keychainToSearchIdentity = CFArrayCreate(kCFAllocatorDefault, (const void **) &myKeychain, 1, &kCFTypeArrayCallBacks);
const void *keys[] = {kSecMatchSearchList, kSecClass, kSecReturnRef, kSecMatchIssuers};
const void *values[] = { keychainToSearchIdentity, kSecClassIdentity, kCFBooleanTrue, mySearchCriteriaIssuers };
CFDictionaryRef searchItemDict = CFDictionaryCreeate(NULL, keys, values, 4 ,NULL, NULL);
SecItemCopyMatch(searchItemDict, &myIdentity);

How to use EndpointSecurity API properly in MacOS 10.15?

I added entitlement and disable SIP :com.apple.developer.endpoint-security.client still get ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED,How to use
EndpointSecurity API properly under Xcode 11?Do I need to run as root?
Code for your reference:
#import <EndpointSecurity/EndpointSecurity.h>
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
es_client_t *client;
es_new_client_result_t res = es_new_client(&client, ^(es_client_t *client, const es_message_t *message) {
NSLog(#"Hi");
});
if (res == ES_NEW_CLIENT_RESULT_SUCCESS){
NSApplication.sharedApplication.keyWindow.contentView.layer.backgroundColor = NSColor.redColor.CGColor;
NSLog(#"ES_NEW_CLIENT_RESULT_SUCCESS");
}else if(res == ES_NEW_CLIENT_RESULT_ERR_INTERNAL){
NSLog(#"ES_NEW_CLIENT_RESULT_ERR_INTERNAL");
}else if(res == ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED){
NSLog(#"ES_NEW_CLIENT_RESULT_ERR_NOT_ENTITLED");
}else if(res == ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED){
NSLog(#"ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED");
}else if(res == ES_NEW_CLIENT_RESULT_ERR_INVALID_ARGUMENT){
NSLog(#"ES_NEW_CLIENT_RESULT_ERR_INVALID_ARGUMENT");
}
BOOL isSubscribeSuccess = es_subscribe(client, ES_EVENT_TYPE_NOTIFY_OPEN);
if (isSubscribeSuccess == true){
NSLog(#"isSubscribeSuccess");
}else{
NSLog(#"SubscribeErrpr");
}
}
In entitlements:
<key>com.apple.developer.endpoint-security.client</key>
<true/>
Yes, all you need is run as root.
Or set the debug process as root by tweaking scheme settings. (Product > Scheme > Edit Scheme...)
According to EndpointSecurity/ESTypes.h:
///The caller is not permitted to connect. They lack Transparency,
Consent, and Control (TCC) approval form the user.
So, ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED indicates that your process was not approved by the user as is required for using EndpointSecurity API. The documentation for es_new_client in EndpointSecurity/ESClient.h states:
The application calling this interface must also be approved by users
via Transparency, Consent & Control (TCC) mechanisms using
the Privacy Preferences pane and adding the application to Full Disk
Access.
A dialog asking the user for permission should come up automatically when you call EndpointSecurity API from a system extension. Otherwise, the user must allow its use manually. BTW, for testing purposes with a command line tool calling EndpointSecurity API I had to add Terminal to Full Disk Access.

SecItemCopyMatching can't read iCloud keychain

I grabbed code off Stack Overflow to access some web browser passwords. It works great as long as the passwords are in the login keychain. At some point, the particular account I am interested in got moved to the iCloud keychain and no longer exists in the login keychain. SecItemCopyMatching can't find it. It returns OSStatus -23500 which is "item not found". How can I access those entries?
CFArrayRef result = NULL;
NSDictionary *params = #{ (__bridge id)kSecClass : (__bridge id)kSecClassInternetPassword,
(__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes : (__bridge id)kCFBooleanTrue,
(__bridge id)kSecAttrProtocol : (__bridge id)kSecAttrProtocolHTTPS,
(__bridge id)kSecAttrServer : #"accounts.mydomain.com"
};
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)(params), (CFTypeRef *) &result);
if (status == noErr) {
// item found!
}
Though on macOS you could see both login and iCloud keychains, former is macOS-style keychain while latter is iOS-style keychain. macOS-style keychain items can be freely accessed by apps as long as the user consents. iOS-style items follow access group policies strictly.
Every key in the iCloud keychain is under an access group. An app. calling SecItemCopyMatching should've said access group as an entitlement for a key to turn up in the query results.
Once you're sure the searched key's access group is among one of the access groups listed under your app's entitlements, there're a couple of attributes you've to pass to SecItemCopyMatching query dictionary (in addition to the mandatory ones like kSecClass):
kSecAttrAccessGroup
kSecAttrSynchronizable
If your app. is entitled to more than one access group, set the right group through kSecAttrAccessGroup. Set kSecAttrSynchronizable to kCFBooleanTrue to search only iOS-style items (which are synchronized); setting it to kSecAttrSynchronizableAny searches both synced and not-synced keys (like the ones in login keychain). Default is to search only local keys (kCFBooleanFalse).
Example Query
This returns an array of dictionaries; one dictionary per key; all iCloud keys your app. has access to turn up. The key-value pairs in each dictionary are corresponding key's properties; this doesn't return the key's value 1
NSString* account = #"my_account";
NSString* service = #"some_service";
NSDictionary* query = #{
(id)kSecClass: (id) kSecClassGenericPassword,
// skip as this example app has only one access group
// (id)kSecAttrAccessGroup: (id) keychainAccessGroup,
(id)kSecAttrSynchronizable: (id)kCFBooleanTrue,
(id)kSecAttrAccount: (id) account,
(id)kSecAttrService: (id) service,
(id)kSecReturnAttributes: (id)kCFBooleanTrue,
(id)kSecMatchLimit: (id)kSecMatchLimitAll,
};
CFTypeRef result;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, &result);
if (status != errSecSuccess) {
CFStringRef err = SecCopyErrorMessageString(status, nil);
NSLog(#"Error: %#", err);
CFRelease(err);
} else {
NSArray* attributes_of_keys = (__bridge NSArray*)result;
}
Inspecting attributes_of_keys under the debugger should tell you plenty.
Source: Apple Developer forum thread, SecItemCopyMatching and beyond.
Apple has done a shoddy job of documenting the SecItem* API family. Information is there but is strewn across different (irrelevant) places (some even outside the official documentation).
1: pass (id)kSecReturnData: (id)kCFBooleanTrue to get key's data; however you can't use kSecReturnData and kSecMatchLimitAll together

iOS 6 contacts access alert never showed on debug

My app on the appstore is accessing the iPhone contacts, after the users downloaded it on iOS 6 it can't access the iPhone contacts while its working fine on iOS 5
the problem is the new privacy settings apple has put in iOS 6 .. so after searching i found out that i have to do the following in my code to be able to access the user contacts:
//in order to test addressbook availability we have to attempt to create an addressbook instance using ABAddressBookCreateWithOptions
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef,
^(bool granted, CFErrorRef error) {
if (granted)
[self loadContacts];
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self loadContact];
} else {
}
#endif //end iOS6+
//ABAddressBookCreateWithOptions not available or succeeded. return YES;
[self loadContacts];
My problem now is while debugging on the device, the alert is not showing, i don't know why ?
I know that the above code should work fine, but only when the app is submitted to the appstore but i want to test that in debug mode before submission ?
Any advice ?
Appreciate your support.
Thanks.
I have managed to get it resolved
Here is the new code after a slight modification:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
__block MyClassType *controller = self;
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef,
^(bool granted, CFErrorRef error) {
if (granted)
[controller loadContacts];
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self loadContacts];
} else {
}
#else
[self loadContacts];
#endif
The key to be able to test it is to Reset the Privacy and location settings from Settings>>General>>Reset>>Reset Location & Privacy
It worked fine with me.
Resetting Location & Privacy doesn't work for me.
My authorization status is always kABAuthorizationStatusAuthorized, regardless of whether I clear simulator settings and then reset Location and Privacy.

Resources