1/* 2 * Copyright (c) 2008-2009 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 * 23 * access_utils.c 24 */ 25 26#include "access_utils.h" 27#include "security.h" 28#include <stdio.h> 29#include <Security/SecKeychainItem.h> 30#include <Security/SecAccess.h> 31#include <Security/SecACL.h> 32 33// create_access 34// 35// This function creates a SecAccessRef with an array of trusted applications. 36// 37int 38create_access(const char *accessName, Boolean allowAny, CFArrayRef trustedApps, SecAccessRef *access) 39{ 40 int result = 0; 41 CFArrayRef appList = NULL; 42 CFArrayRef aclList = NULL; 43 CFStringRef description = NULL; 44 const char *descriptionLabel = (accessName) ? accessName : "<unlabeled key>"; 45 CFStringRef promptDescription = NULL; 46 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector; 47 SecACLRef aclRef; 48 OSStatus status; 49 50 if (accessName) { 51 description = CFStringCreateWithCString(NULL, descriptionLabel, kCFStringEncodingUTF8); 52 } 53 54 status = SecAccessCreate(description, trustedApps, access); 55 if (status) 56 { 57 sec_perror("SecAccessCreate", status); 58 result = 1; 59 goto cleanup; 60 } 61 62 // get the access control list for decryption operations (this controls access to an item's data) 63 status = SecAccessCopySelectedACLList(*access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList); 64 if (status) 65 { 66 sec_perror("SecAccessCopySelectedACLList", status); 67 result = 1; 68 goto cleanup; 69 } 70 71 // get the first entry in the access control list 72 aclRef = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0); 73 status = SecACLCopySimpleContents(aclRef, &appList, &promptDescription, &promptSelector); 74 if (status) 75 { 76 sec_perror("SecACLCopySimpleContents", status); 77 result = 1; 78 goto cleanup; 79 } 80 81 if (allowAny) // "allow all applications to access this item" 82 { 83 // change the decryption ACL to not require the passphrase, and have a nil application list. 84 promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE; 85 status = SecACLSetSimpleContents(aclRef, NULL, promptDescription, &promptSelector); 86 } 87 else // "allow access by these applications" 88 { 89 // modify the application list 90 status = SecACLSetSimpleContents(aclRef, trustedApps, promptDescription, &promptSelector); 91 } 92 if (status) 93 { 94 sec_perror("SecACLSetSimpleContents", status); 95 result = 1; 96 goto cleanup; 97 } 98 99cleanup: 100 if (description) 101 CFRelease(description); 102 if (promptDescription) 103 CFRelease(promptDescription); 104 if (appList) 105 CFRelease(appList); 106 if (aclList) 107 CFRelease(aclList); 108 109 return result; 110} 111 112// merge_access 113// 114// This function merges the contents of otherAccess into access. 115// Simple ACL contents are assumed, and only the standard ACL 116// for decryption operations is currently processed. 117// 118int 119merge_access(SecAccessRef access, SecAccessRef otherAccess) 120{ 121 OSStatus status; 122 CFArrayRef aclList, newAclList; 123 124 // get existing access control list for decryption operations (this controls access to an item's data) 125 status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList); 126 if (status) { 127 return status; 128 } 129 // get desired access control list for decryption operations 130 status = SecAccessCopySelectedACLList(otherAccess, CSSM_ACL_AUTHORIZATION_DECRYPT, &newAclList); 131 if (status) { 132 newAclList = nil; 133 } else { 134 SecACLRef aclRef=(SecACLRef)CFArrayGetValueAtIndex(aclList, 0); 135 SecACLRef newAclRef=(SecACLRef)CFArrayGetValueAtIndex(newAclList, 0); 136 CFArrayRef appList=nil; 137 CFArrayRef newAppList=nil; 138 CFMutableArrayRef mergedAppList = nil; 139 CFStringRef promptDescription=nil; 140 CFStringRef newPromptDescription=nil; 141 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector; 142 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR newPromptSelector; 143 144 status = SecACLCopySimpleContents(aclRef, &appList, &promptDescription, &promptSelector); 145 if (!status) { 146 status = SecACLCopySimpleContents(newAclRef, &newAppList, &newPromptDescription, &newPromptSelector); 147 } 148 if (!status) { 149 if (appList) { 150 mergedAppList = CFArrayCreateMutableCopy(NULL, 0, appList); 151 } 152 if (newAppList) { 153 if (mergedAppList) { 154 CFArrayAppendArray(mergedAppList, newAppList, CFRangeMake(0, CFArrayGetCount(newAppList))); 155 } else { 156 mergedAppList = CFArrayCreateMutableCopy(NULL, 0, newAppList); 157 } 158 } 159 promptSelector.flags = newPromptSelector.flags; 160 status = SecACLSetSimpleContents(aclRef, mergedAppList, newPromptDescription, &newPromptSelector); 161 } 162 163 if (appList) CFRelease(appList); 164 if (newAppList) CFRelease(newAppList); 165 if (mergedAppList) CFRelease(mergedAppList); 166 if (promptDescription) CFRelease(promptDescription); 167 if (newPromptDescription) CFRelease(newPromptDescription); 168 } 169 if (aclList) CFRelease(aclList); 170 if (newAclList) CFRelease(newAclList); 171 172 return status; 173} 174 175// modify_access 176// 177// This function updates the access for an existing item. 178// The provided access is merged with the item's existing access. 179// 180int 181modify_access(SecKeychainItemRef itemRef, SecAccessRef access) 182{ 183 OSStatus status; 184 SecAccessRef curAccess = NULL; 185 // for historical reasons, we have to modify the item's existing access reference 186 // (setting the item's access to a freshly created SecAccessRef instance doesn't behave as expected) 187 status = SecKeychainItemCopyAccess(itemRef, &curAccess); 188 if (status) { 189 sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status)); 190 } else { 191 status = merge_access(curAccess, access); // make changes to the existing access reference 192 if (!status) { 193 status = SecKeychainItemSetAccess(itemRef, curAccess); // will prompt! 194 if (status) { 195 sec_error("SecKeychainItemSetAccess: %s", sec_errstr(status)); 196 } 197 } 198 } 199 if (curAccess) { 200 CFRelease(curAccess); 201 } 202 return status; 203} 204