X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/84aacf34eae6543be9f0280b2015385f91e5c2c6..b54c578e17e9bcbd74aa30ea75e25e955b9a6205:/SecurityTool/macOS/access_utils.c?ds=inline diff --git a/SecurityTool/macOS/access_utils.c b/SecurityTool/macOS/access_utils.c new file mode 100644 index 00000000..06781a27 --- /dev/null +++ b/SecurityTool/macOS/access_utils.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2008-2009,2012,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * access_utils.c + */ + +#include "access_utils.h" +#include "security_tool.h" +#include +#include +#include +#include + +// create_access +// +// This function creates a SecAccessRef with an array of trusted applications. +// +int +create_access(const char *accessName, Boolean allowAny, CFArrayRef trustedApps, SecAccessRef *access) +{ + int result = 0; + CFArrayRef appList = NULL; + CFArrayRef aclList = NULL; + CFStringRef description = NULL; + const char *descriptionLabel = (accessName) ? accessName : ""; + CFStringRef promptDescription = NULL; + CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector; + SecACLRef aclRef; + OSStatus status; + + if (accessName) { + description = CFStringCreateWithCString(NULL, descriptionLabel, kCFStringEncodingUTF8); + } + + status = SecAccessCreate(description, trustedApps, access); + if (status) + { + sec_perror("SecAccessCreate", status); + result = 1; + goto cleanup; + } + + // get the access control list for decryption operations (this controls access to an item's data) + status = SecAccessCopySelectedACLList(*access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList); + if (status) + { + sec_perror("SecAccessCopySelectedACLList", status); + result = 1; + goto cleanup; + } + + // get the first entry in the access control list + aclRef = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0); + status = SecACLCopySimpleContents(aclRef, &appList, &promptDescription, &promptSelector); + if (status) + { + sec_perror("SecACLCopySimpleContents", status); + result = 1; + goto cleanup; + } + + if (allowAny) // "allow all applications to access this item" + { + // change the decryption ACL to not require the passphrase, and have a nil application list. + promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE; + status = SecACLSetSimpleContents(aclRef, NULL, promptDescription, &promptSelector); + } + else // "allow access by these applications" + { + // modify the application list + status = SecACLSetSimpleContents(aclRef, trustedApps, promptDescription, &promptSelector); + } + if (status) + { + sec_perror("SecACLSetSimpleContents", status); + result = 1; + goto cleanup; + } + +cleanup: + if (description) + CFRelease(description); + if (promptDescription) + CFRelease(promptDescription); + if (appList) + CFRelease(appList); + if (aclList) + CFRelease(aclList); + + return result; +} + +// merge_access +// +// This function merges the contents of otherAccess into access. +// Simple ACL contents are assumed, and only the standard ACL +// for decryption operations is currently processed. +// +int +merge_access(SecAccessRef access, SecAccessRef otherAccess) +{ + OSStatus status; + CFArrayRef aclList, newAclList; + + // get existing access control list for decryption operations (this controls access to an item's data) + status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList); + if (status) { + return status; + } + // get desired access control list for decryption operations + status = SecAccessCopySelectedACLList(otherAccess, CSSM_ACL_AUTHORIZATION_DECRYPT, &newAclList); + if (status) { + newAclList = nil; + } else { + SecACLRef aclRef=(SecACLRef)CFArrayGetValueAtIndex(aclList, 0); + SecACLRef newAclRef=(SecACLRef)CFArrayGetValueAtIndex(newAclList, 0); + CFArrayRef appList=nil; + CFArrayRef newAppList=nil; + CFMutableArrayRef mergedAppList = nil; + CFStringRef promptDescription=nil; + CFStringRef newPromptDescription=nil; + CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector; + CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR newPromptSelector; + + status = SecACLCopySimpleContents(aclRef, &appList, &promptDescription, &promptSelector); + if (!status) { + status = SecACLCopySimpleContents(newAclRef, &newAppList, &newPromptDescription, &newPromptSelector); + } + if (!status) { + if (appList) { + mergedAppList = CFArrayCreateMutableCopy(NULL, 0, appList); + } + if (newAppList) { + if (mergedAppList) { + CFArrayAppendArray(mergedAppList, newAppList, CFRangeMake(0, CFArrayGetCount(newAppList))); + } else { + mergedAppList = CFArrayCreateMutableCopy(NULL, 0, newAppList); + } + } + promptSelector.flags = newPromptSelector.flags; + status = SecACLSetSimpleContents(aclRef, mergedAppList, newPromptDescription, &newPromptSelector); + } + + if (appList) CFRelease(appList); + if (newAppList) CFRelease(newAppList); + if (mergedAppList) CFRelease(mergedAppList); + if (promptDescription) CFRelease(promptDescription); + if (newPromptDescription) CFRelease(newPromptDescription); + } + if (aclList) CFRelease(aclList); + if (newAclList) CFRelease(newAclList); + + return status; +} + +// modify_access +// +// This function updates the access for an existing item. +// The provided access is merged with the item's existing access. +// +int +modify_access(SecKeychainItemRef itemRef, SecAccessRef access) +{ + OSStatus status; + SecAccessRef curAccess = NULL; + // for historical reasons, we have to modify the item's existing access reference + // (setting the item's access to a freshly created SecAccessRef instance doesn't behave as expected) + status = SecKeychainItemCopyAccess(itemRef, &curAccess); + if (status) { + sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status)); + } else { + status = merge_access(curAccess, access); // make changes to the existing access reference + if (!status) { + status = SecKeychainItemSetAccess(itemRef, curAccess); // will prompt! + if (status) { + sec_error("SecKeychainItemSetAccess: %s", sec_errstr(status)); + } + } + } + if (curAccess) { + CFRelease(curAccess); + } + return status; +}