/ SecurityTool / macOS / access_utils.c
access_utils.c
  1  /*
  2   * Copyright (c) 2008-2009,2012,2014 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_tool.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  //
 37  int
 38  create_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  
 99  cleanup:
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  //
118  int
119  merge_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  //
180  int
181  modify_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  }