I've got an application that also configures and runs a daemon. I am trying to give both the daemon and the application access permissions to the keychain item. The basic code:
SecKeychainItemRef item;
// create a generic password item
SecTrustedApplicationRef appRef[2];
SecAccessRef ref;
SecTrustedApplicationCreateFromPath( NULL, &appRef[0] );
SecTrustedApplicationCreateFromPath( DAEMON_PATH, &appRef[1] );
CFArrayRef trustList = CFArrayCreate( NULL, ( void *)appRef, sizeof(appRef)/sizeof(*appRef), NULL );
SecAccessCreate( descriptor, trustList, &ref );
SecKeychainItemSetAccess( item, ref );
The keychain entry is created, however the only application listed in the Keychain Access tool as always having access is the main application. Let's call it FOO.app. DAEMON_PATH points to the absolute path of the daemon which is in the application bundle -- call it FOO.daemon.
If I manually go within Keychain Access and select the daemon, it does get added to the list.
Any idea on how to get SecTrustedApplicationCreateFromPath to honor the full/absolute path?
If you need an answer today...
I tried to replace access object for existing keychain item with no success, too. So, I decided to modify existing access object rather than replace it, and this approach works well.
The following pseudocode demonstrates the idea. Declarations, CFRelease()s and error checking are stripped for clarity sake.
SecKeychainItemCopyAccess(item, &accessObj);
SecAccessCopySelectedACLList(accessObj, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
assert(CFArrayGetCount(aclList) == 1);
acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
SecACLCopySimpleContents(acl, &appList, &desc, &prompt_selector);
SecTrustedApplicationCreateFromPath(MY_APP_PATH, &app);
newAppList = CFArrayCreate(NULL, (const void**)&app, 1, NULL);
SecACLSetSimpleContents(acl, newAppList, desc, &psel);
SecKeychainItemSetAccess(item, accessObj);
I used SecAccessCopySelectedACLList to search for an ACL object with an appropriate authorization tag. You may require some other way for ACL filtering.
The straightforward access object creation must be more tricky, you are to create the same ACL structure as Keychain Access app does, rather than use default SecAccessCreate()'s ACLs. I couldn't cope with that way.
Related
I know the response to this question is "set it in the info.plist" but if I understand the way this works then if the user rejects the permissions you are requesting they don't get prompted again which effectively bricks your app unless you ...write code to check for permissions. Is that right? So I don't know why everyone acts like it's just automatic on iOS. You still have to check for permissions, right?
Assuming I am right, where can I find documentation on how to do it correctly. I checked this (solution was plugin I don't want to use), this (code is too complicated), this (long-winded non-answer), and this plus a few others from google which point to this plugin which I don't want to use. I just want a link to the documentation on how to check and request permissions on iOS. Is there such a link?
What I have looks like this:
private bool HasLocationPermission()
{
return CoreLocation.CLLocationManager.Status == CoreLocation.CLAuthorizationStatus.Authorized ||
CoreLocation.CLLocationManager.Status == CoreLocation.CLAuthorizationStatus.AuthorizedAlways ||
CoreLocation.CLLocationManager.Status == CoreLocation.CLAuthorizationStatus.AuthorizedWhenInUse;
}
but, of course, that is just for the "Location" permission. I don't see any information about what to check for in the LocationManager documentation. There's something about request rational or something? Where can I find how to do this? No plugins please.
If you study the code in the Permissions Plugin you linked to you can pretty easily deduct what you have to do.
In iOS there are 2 different types of location permissions, for either of them to work, you need to set up some descriptions in your Info.plist, which will be shown when prompted the permission dialog.
Set up either NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription depending on your needs. The distinction between this mode is. When in use, is for when occasionally needing location services, i.e. for briefly showing a map. While always is typically for apps needing it to track the users location all the time.
The section in the Info.plist will look something like:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Do you want My App to access your location?</string>
As you've already figured out you can use CoreLocation and CLLocationManager to get the current permission status:
var locationManager = new CLLocationManager();
var status = locationManager.Status;
Status will either be:
NotDetermined - the App doesn't have permission yet or maybe never asked for it.
AuthorizedAlways - if you requested Always location and added the key NSLocationAlwaysUsageDescription.
AuthorizedWhenInUse - if you requested When In Use location and added the key NSLocationWhenInUseUsageDescription
Denied - user said "no thank you"
If you want to request the permission, simply call:
locationManager.RequestAlwaysAuthorization(); // for always
locationManager.RequestWhenInUseAuthorization(); // for when in use
You can listen to Authorization changes with the AuthorizationChanged event:
locationManager.AuthorizationChanged += OnAuthorizationChanged;
The CLAuthorizationChangedEventArgs will provide you the new status. You may want to hook up this even before requesting the permission.
private void OnAuthorizationChanged(object sender, CLAuthorizationChangedEventArgs args)
{
if (args.Status == CLAuthorizationStatus.AuthorizedAlways ||
args.Status == CLAuthorizationStatus.AuthorizedWhenInUse)
{
// all green, you are good to start listening to location changes!
}
}
Now you can start listening to location changes:
locationManager.LocationsUpdated += OnLocationsUpdated;
locationManager.StartUpdatingLocation();
I have a SecTrustRef object from the system that I'd like to evaluate myself. Just calling SecTrustEvaluateAsync will be sufficient for this job. The problem is, I must evaluate it in a different process as only this other process has access to the keychains where the CA certificates are stored that may cause evaluation to succeed.
The two processes have an IPC link that allows me to exchange arbitrary byte data between them but I don't see any way to easily serialize a SecTrustRef into byte data and deserialize that data back to an object at the other process. There doesn't seem to be a persistent storage mechanism for SecTrustRef.
So am I overlooking something important here, or do I really have to get all the certs (SecTrustGetCertificateAtIndex) and all the policies (SecTrustCopyPolicies) and serialize these myself?
And if so, how would I serialize a policy?
For the certificate (SecCertificateRef) it's rather easy, I just call SecCertificateCopyData and later on SecCertificateCreateWithData.
But for policies I can only call SecPolicyCopyProperties on one side and later on SecPolicyCreateWithProperties, however the later one requires a 2nd parameter, a policyIdentifier and I see no way to get that value from an existing policy. What am I missing?
Reading through the source of the Security framework, I finally figured it out how to copy a SecPolicyRef:
SecPolicyCreateWithProperties wants what it calls a "policyIdentifier". It's a constant like kSecPolicyAppleIPsec.
This does not get stored directly by the function, it's comparing the value and calling dedicated internal "initializers" (like SecPolicyCreateIPsec).
These in turn call SecPolicyCreate (which is private). They end up passing the same identifier value that you passed to SecPolicyCreateWithProperties.
And this value then gets stored as-is in the _oid field!
The identifier is actually the OID. You can get it either via SecPolicyCopyProperties(policy) (stored in the dictionary with key kSecPolicyOid) or via SecPolicyGetOID (but that returns it as an inconvenient CSSM_OID). Some of those specialized initializers also use values from the properties dictionary passed to SecPolicyCreateWithProperties, those should be present in the copied properties dictionary already.
So this gives us:
Serialization:
CFDictionaryRef createSerializedPolicy(SecPolicyRef policy) {
// Already contains all the information needed.
return SecPolicyCopyProperties(policy);
}
Deserialization:
SecPolicyRef createDeserializedPolicy (CFDictionaryRef serialized) {
CFStringRef oid = CFDictionaryGetValue(serialized, kSecPolicyOid);
if (oid == NULL || CFGetTypeID(oid) != CFStringGetTypeID()) {
return NULL;
}
return SecPolicyCreateWithProperties(oid, serialized);
}
To reproduce the original SecTrustRef as closely as possible, the anchors need to be copied as well. There is an internal variable _anchorsOnly which is set to true once you set anchors. Unfortunately, there is no way to query this value and I've seen it being false in trusts passed by NSURLSession, for example. No idea yet on how to get this value in a public way.
Another problematic bit are the exceptions: if _exceptions is NULL but you query them via SecTrustCopyExceptions(trust), you do get data! And if you assign that to the deserialized trust via SecTrustSetExceptions(trust, exceptions) you suddenly end up with exceptions that were not there before and can change the evaluation result! (I've seen those suddenly appearing exceptions lead to an evaluation result of "proceed" instead of "recoverable trust failure").
(Note: IMO the question is mainly about WinAPI and DACL and not about CNG, so please read on!)
I'm currently trying to modify the sample CNG key storage provider of Microsoft's Cryptographic Provider Development Kit in such a way that it does not store the keys in single files. However, I'm in trouble with the security descriptors that can be assigned to the private keys.
In the Certificates Snap-in of the Windows Server Management Console, private keys of certificates can be managed, i.e. the owner, DACL and SACL of a key can be changed, which results in a NCryptSetProperty call with a security descriptor as parameter. For the DACL, the snap-in only allows to allow/deny "full control" or "read", which results in the GENERIC_ALL or GENERIC_READ bit to be set in the access mask of the ACE.
As I have learnt, these generic bits need to be mapped to application specific rights - otherwise AccessCheck will not work. But do I really need to do this by hand???
CreatePrivateObjectSecurity+SetPrivateObjectSecurity does not always work since CreatePrivateObjectSecurity is very picky about the owner and group in the input security descriptor. Moreover, when the mapping is applied, the generic bits are cleared in the access mask, which results in the snap-in showing wrong settings (as I said, the snap-in only considers the GA and GR bits when displaying current permissions).
Seems I'm missing some pieces here...
in your CPSetProvParam implementation for PP_KEYSET_SEC_DESCR you got address of a SECURITY_DESCRIPTOR, which you need somehow apply to your private key storage. if your storage based on file(s) or registry key(s) ( in principle any kernel object type, but what more can be used here ?) you need call SetKernelObjectSecurity with file or key HANDLE (which must have WRITE_DAC access) (may be multiple time if you say have multiple files for store single key). in kernel GENERIC access to object will be auto converted to object specific rights.
if your implementation of storage not direct based on some kernel object, but custom - you need yourself at this point convert GENERIC access (0xF0000000 mask) to specific access rights (0x0000FFFF mask)
__________________ EDIT ____________________
after more check i found that provider must not only convert generic to specific access in CPSetProvParam but also convert specific to generic in CPGetProvParam despite this not directly point in documentation.
this is how MS_ENHANCED_PROV (implemented in rsaenh.dll) approximately do this:
void CheckAndChangeAccessMask(PSECURITY_DESCRIPTOR SecurityDescriptor)
{
BOOL bDaclPresent, bDaclDefaulted;
PACL Dacl;
ACL_SIZE_INFORMATION asi;
if (
GetSecurityDescriptorDacl(SecurityDescriptor, &bDaclPresent, &Dacl, &bDaclDefaulted)
&&
bDaclPresent
&&
Dacl
&&
GetAclInformation(Dacl, &asi, sizeof(asi), AclSizeInformation)
&&
asi.AceCount
)
{
union{
PVOID pAce;
PACE_HEADER pah;
PACCESS_ALLOWED_ACE paa;
};
do
{
if (GetAce(Dacl, --asi.AceCount, &pAce))
{
switch (pah->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
case ACCESS_DENIED_ACE_TYPE:
ACCESS_MASK Mask = paa->Mask, Gen_Mask = 0;
if (Mask & FILE_READ_DATA)
{
Gen_Mask |= GENERIC_READ;
}
if (Mask & FILE_WRITE_DATA)
{
Gen_Mask |= GENERIC_ALL;
}
paa->Mask = Gen_Mask;
break;
}
}
} while (asi.AceCount);
}
}
so FILE_READ_DATA converted to GENERIC_READ and FILE_WRITE_DATA to GENERIC_ALL (this is exactly algorithm) - however you can look yourself code of rsaenh.CheckAndChangeAccessMask (name from pdb symbols)
rsaenh first get SD from file by GetNamedSecurityInfoW (SE_FILE_OBJECT) and then convert it specific to generic access.
here the call graph and modified DACL (in the top-right, modified ACCESS_MASK in red color)
I am making a small app that deletes log files. I am using an NSTask instance which runs rm and srm (secure rm) to delete files.
I want to be able to delete files in:
/Library/Logs
~/Library/Logs
The issue is that the user account does not have permissions to access some files in the system library folder, such as the Adobe logs subfolder and others. For example, only the "system" user (group?) has r/w permissions for the Adobe logs folder and its contents, and the current user doesn't even have an entry in the permissions shown in the Get Info window for the folder.
What I want to be able to do exactly:
Obtain admin privileges.
Store the password in the Keychain so the app doesn't have to nag the user each time (Is the storage of the password a bad idea? Is it possible?)
Delete a file whatever the file permissions may be.
I am using NSTask because it offers notifications for task completion, getting text output from the task itself, etc. Would I need to use something else? If so, how could I replicate NSTask's completion notifications and output file handle while running rm and srm with admin privileges?
I am looking for the most secure way to handle the situation. i.e. I don't want my application to become a doorway for privilege escalation attacks.
I looked at the Authorization Services Programming Guide but I am not sure which case fits. At first I thought that AuthorizationExecuteWithPrivileges would be a good idea but after reading more on the subject it looks like this method is not recommended for security reasons.
A detailed answer would be very welcome. I'm sure some of you already had to do something similar and have some code and knowledge to share.
Thanks in advance!
Update:
I am now able to make the authentication dialog pop up and obtain privileges, like so:
OSStatus status;
AuthorizationRef authRef;
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef);
AuthorizationRights authRights;
AuthorizationItem authItems[1];
authItems[0].name = kAuthorizationRightExecute;
authRights.count = sizeof(authItems) / sizeof(authItems[0]);
authRights.items = authItems;
AuthorizationFlags authFlags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed;
status = AuthorizationCopyRights(authRef, &authRights, kAuthorizationEmptyEnvironment, authFlags, NULL);
From the looks of it, it seems that the "Factored Application" method looks the most appropriate. The thing is that, to me, rm already seems like an external helper tool. I'm not sure I get the setuid alternative suggested in the documentation. Could I set the setuid bit on rm and run it using the NSTask method I already implemented? This would mean that I wouldn't need to create my own helper tool. Could somebody elaborate on this subject?
I also looked at the BetterAuthorizationSample which is suggested as a more secure and recent alternative to the setuid bit method, but found it awfully complex for such as simple behavior. Any hints?
Thanks in advance for any help!
Perhaps a tad late, but this might be useful for future reference for other people. Most of the code is from this person.
Basically, it has a lot to do with Authorization on the Mac. You can read more about that here and here.
The code, which uses the rm tool:
+ (BOOL)removeFileWithElevatedPrivilegesFromLocation:(NSString *)location
{
// Create authorization reference
OSStatus status;
AuthorizationRef authorizationRef;
// AuthorizationCreate and pass NULL as the initial
// AuthorizationRights set so that the AuthorizationRef gets created
// successfully, and then later call AuthorizationCopyRights to
// determine or extend the allowable rights.
// http://developer.apple.com/qa/qa2001/qa1172.html
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
if (status != errAuthorizationSuccess)
{
NSLog(#"Error Creating Initial Authorization: %d", status);
return NO;
}
// kAuthorizationRightExecute == "system.privilege.admin"
AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights rights = {1, &right};
AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
// Call AuthorizationCopyRights to determine or extend the allowable rights.
status = AuthorizationCopyRights(authorizationRef, &rights, NULL, flags, NULL);
if (status != errAuthorizationSuccess)
{
NSLog(#"Copy Rights Unsuccessful: %d", status);
return NO;
}
// use rm tool with -rf
char *tool = "/bin/rm";
char *args[] = {"-rf", (char *)[location UTF8String], NULL};
FILE *pipe = NULL;
status = AuthorizationExecuteWithPrivileges(authorizationRef, tool, kAuthorizationFlagDefaults, args, &pipe);
if (status != errAuthorizationSuccess)
{
NSLog(#"Error: %d", status);
return NO;
}
// The only way to guarantee that a credential acquired when you
// request a right is not shared with other authorization instances is
// to destroy the credential. To do so, call the AuthorizationFree
// function with the flag kAuthorizationFlagDestroyRights.
// http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/02authconcepts/chapter_2_section_7.html
status = AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
return YES;
}
I had this headache a few months ago. I was trying to get a shell script running with admin privileges that shutdown my computer at a certain time. I feel your pain.
I used the BetterAuthorizationSample which was a total nightmare to wade through. But I took the most pragmatic route - I didn't bother trying to understand everything that was going on, I just grabbed the guts of the code.
It didn't take me that long to get it doing what I wanted. I can't remember exactly what I altered, but you're welcome to check out my code:
http://github.com/johngallagher/TurnItOff
I hope this helps on your quest for a secure application!
I'm using authopen inside one of my programs to modify files owned by root. As can be seen in the screenshot below authopen asks for a admin password. What I'd like to achieve is that the dialog shows my app's name and then passes the authorization to authopen.
Code
Launching authopen which returns an authorized file descriptor.
int pipe[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, pipe);
if (fork() == 0) { // child
// close parent's pipe
close(pipe[0]);
dup2(pipe[1], STDOUT_FILENO);
const char *authopenPath = "/usr/libexec/authopen";
execl(authopenPath,
authopenPath,
"-stdoutpipe",
[self.device.devicePath fileSystemRepresentation],
NULL);
NSLog(#"Fatal error, we should never reach %s:%d", __FILE__, __LINE__);
exit(-1);
} else { // parent
close(pipe[1]);
}
// get file descriptor through sockets
I'd really like not to use AuthorizationExecuteWithPrivileges because then I'd have to get more rights than I want to.
Apple has added an option to authopen in OS X 10.9 Mavericks that allows exactly this. Prior this seemed to be impossible.
-extauth
specifies that authopen should read one AuthorizationExternalForm structure from stdin,
convert it to an AuthorizationRef, and attempt to use it to authorize the open(2)
operation.
The authorization should refer to the sys.openfile right corresponding to the requested operation.
The authorization data will be read before any additional data supplied on stdin, and will
not be included in data written with -w.
I have not used this yet, so I do not have any sample code. If someone has, please add it to this answer.
I think if you give your app path in the 1st arg:
execl(authopenPath,
"app path", // <--
"-stdoutpipe",
[self.device.devicePath fileSystemRepresentation],
NULL);
the dialog will show:
"app namerequires that you type your password"
You need to be looking directly at the security framework, introduced in 10.4, I think, and been the main authorization source since 10.5. OSX still works within PAM (like Linux), but /etc/authorization now supersedes this. Apple does have one or two samples of code on how you could pragmatically create a class/entry for someone to authorize themselves against (or preauthorize/be preauthorized like folks who are allowed to print).
This question is old but it seems to be possible, as explained in this technical note :
Technical Note TN2095 : Authorization for Everyone
http://developer.apple.com/library/mac/#technotes/tn2095/_index.html