]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTool/macOS/access_utils.c
Security-59306.11.20.tar.gz
[apple/security.git] / SecurityTool / macOS / access_utils.c
diff --git a/SecurityTool/macOS/access_utils.c b/SecurityTool/macOS/access_utils.c
new file mode 100644 (file)
index 0000000..06781a2
--- /dev/null
@@ -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 <stdio.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecAccess.h>
+#include <Security/SecACL.h>
+
+// 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 : "<unlabeled key>";
+       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;
+}