--- /dev/null
+/*
+ * Copyright (c) 2004-2006 Apple Computer, 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@
+ */
+
+/*
+ * aclUtils.cpp - ACL utility functions, copied from the SecurityTool project.
+ */
+
+#include "aclUtils.h"
+#include <Security/SecTrustedApplicationPriv.h>
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+
+/* Read a line from stdin into buffer as a null terminated string. If buffer is
+ non NULL use at most buffer_size bytes and return a pointer to buffer. Otherwise
+ return a newly malloced buffer.
+ if EOF is read this function returns NULL. */
+char *
+readline(char *buffer, int buffer_size)
+{
+ int ix = 0, bytes_malloced = 0;
+
+ if (!buffer)
+ {
+ bytes_malloced = 64;
+ buffer = (char *)malloc(bytes_malloced);
+ buffer_size = bytes_malloced;
+ }
+
+ for (;;++ix)
+ {
+ int ch;
+
+ if (ix == buffer_size - 1)
+ {
+ if (!bytes_malloced)
+ break;
+ bytes_malloced += bytes_malloced;
+ buffer = (char *)realloc(buffer, bytes_malloced);
+ buffer_size = bytes_malloced;
+ }
+
+ ch = getchar();
+ if (ch == EOF)
+ {
+ if (bytes_malloced)
+ free(buffer);
+ return NULL;
+ }
+ if (ch == '\n')
+ break;
+ buffer[ix] = ch;
+ }
+
+ /* 0 terminate buffer. */
+ buffer[ix] = '\0';
+
+ return buffer;
+}
+
+void
+print_buffer_hex(FILE *stream, UInt32 length, const void *data)
+{
+ unsigned i;
+ const unsigned char *cp = (const unsigned char *)data;
+
+ printf("\n ");
+ for(i=0; i<length; i++) {
+ fprintf(stream, "%02X ", cp[i]);
+ if((i % 24) == 23) {
+ printf("\n ");
+ }
+ }
+}
+
+void
+print_buffer_ascii(FILE *stream, UInt32 length, const void *data)
+{
+ uint8 *p = (uint8 *) data;
+ while (length--)
+ {
+ int ch = *p++;
+ if (ch >= ' ' && ch <= '~' && ch != '\\')
+ {
+ fputc(ch, stream);
+ }
+ else
+ {
+ fputc('\\', stream);
+ fputc('0' + ((ch >> 6) & 7), stream);
+ fputc('0' + ((ch >> 3) & 7), stream);
+ fputc('0' + ((ch >> 0) & 7), stream);
+ }
+ }
+}
+
+void
+print_buffer(FILE *stream, UInt32 length, const void *data)
+{
+ uint8 *p = (uint8 *) data;
+ Boolean ascii = TRUE; // unless we determine otherwise
+ UInt32 ix;
+ for (ix = 0; ix < length; ++ix) {
+ int ch = *p++;
+ if ((ch < ' ') || (ch > '~')) {
+ if((ch == 0) && (ix == (length - 1))) {
+ /* ignore trailing null */
+ length--;
+ break;
+ }
+ ascii = FALSE;
+ break;
+ }
+ }
+
+ if (ascii) {
+ fputc('"', stream);
+ print_buffer_ascii(stream, length, data);
+ fputc('"', stream);
+ }
+ else {
+ print_buffer_hex(stream, length, data);
+ }
+}
+
+void
+print_cfdata(FILE *stream, CFDataRef data)
+{
+ if (data)
+ return print_buffer(stream, CFDataGetLength(data), CFDataGetBytePtr(data));
+ else
+ fprintf(stream, "<NULL>");
+}
+
+void
+print_cfstring(FILE *stream, CFStringRef string)
+{
+ if (!string)
+ fprintf(stream, "<NULL>");
+ else
+ {
+ const char *utf8 = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
+ if (utf8)
+ fprintf(stream, "%s", utf8);
+ else
+ {
+ CFRange rangeToProcess = CFRangeMake(0, CFStringGetLength(string));
+ while (rangeToProcess.length > 0)
+ {
+ UInt8 localBuffer[256];
+ CFIndex usedBufferLength;
+ CFIndex numChars = CFStringGetBytes(string, rangeToProcess,
+ kCFStringEncodingUTF8, '?', FALSE, localBuffer,
+ sizeof(localBuffer), &usedBufferLength);
+ if (numChars == 0)
+ break; // Failed to convert anything...
+
+ fprintf(stream, "%.*s", (int)usedBufferLength, localBuffer);
+ rangeToProcess.location += numChars;
+ rangeToProcess.length -= numChars;
+ }
+ }
+ }
+}
+
+
+int
+print_access(FILE *stream, SecAccessRef access, Boolean interactive)
+{
+ CFArrayRef aclList = NULL;
+ CFIndex aclix, aclCount;
+ int result = 0;
+ OSStatus status;
+
+ status = SecAccessCopyACLList(access, &aclList);
+ if (status)
+ {
+ cssmPerror("SecAccessCopyACLList", status);
+ result = 1;
+ goto loser;
+ }
+
+ aclCount = CFArrayGetCount(aclList);
+ fprintf(stream, "access: %lu entries\n", aclCount);
+ for (aclix = 0; aclix < aclCount; ++aclix)
+ {
+ CFArrayRef applicationList = NULL;
+ CFStringRef description = NULL;
+ CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {};
+ CFIndex appix, appCount;
+
+ SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, aclix);
+ CSSM_ACL_AUTHORIZATION_TAG tags[64]; // Pick some upper limit
+ uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags);
+ status = SecACLGetAuthorizations(acl, tags, &tagCount);
+ if (status)
+ {
+ cssmPerror("SecACLGetAuthorizations", status);
+ result = 1;
+ goto loser;
+ }
+
+ fprintf(stream, " entry %lu:\n authorizations (%lu):", aclix, (unsigned long)tagCount);
+ for (tagix = 0; tagix < tagCount; ++tagix)
+ {
+ CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix];
+ switch (tag)
+ {
+ case CSSM_ACL_AUTHORIZATION_ANY:
+ fputs(" any", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_LOGIN:
+ fputs(" login", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_GENKEY:
+ fputs(" genkey", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DELETE:
+ fputs(" delete", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED:
+ fputs(" export_wrapped", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR:
+ fputs(" export_clear", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED:
+ fputs(" import_wrapped", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR:
+ fputs(" import_clear", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_SIGN:
+ fputs(" sign", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_ENCRYPT:
+ fputs(" encrypt", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DECRYPT:
+ fputs(" decrypt", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_MAC:
+ fputs(" mac", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DERIVE:
+ fputs(" derive", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DBS_CREATE:
+ fputs(" dbs_create", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DBS_DELETE:
+ fputs(" dbs_delete", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DB_READ:
+ fputs(" db_read", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DB_INSERT:
+ fputs(" db_insert", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DB_MODIFY:
+ fputs(" db_modify", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_DB_DELETE:
+ fputs(" db_delete", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_CHANGE_ACL:
+ fputs(" change_acl", stream);
+ break;
+ case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER:
+ fputs(" change_owner", stream);
+ break;
+ default:
+ fprintf(stream, " tag=%lu", (unsigned long)tag);
+ break;
+ }
+ }
+ fputc('\n', stream);
+
+ status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector);
+ if (status)
+ {
+ cssmPerror("SecACLCopySimpleContents", status);
+ continue;
+ }
+
+ if (promptSelector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE)
+ fputs(" require-password\n", stream);
+ else
+ fputs(" don't-require-password\n", stream);
+
+ fputs(" description: ", stream);
+ print_cfstring(stream, description);
+ fputc('\n', stream);
+
+ if (applicationList)
+ {
+ appCount = CFArrayGetCount(applicationList);
+ fprintf(stream, " applications (%lu):\n", appCount);
+ }
+ else
+ {
+ appCount = 0;
+ fprintf(stream, " applications: <null>\n");
+ }
+
+ for (appix = 0; appix < appCount; ++appix)
+ {
+ const UInt8* bytes;
+ SecTrustedApplicationRef app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(applicationList, appix);
+ CFDataRef data = NULL;
+ fprintf(stream, " %lu: ", appix);
+ status = SecTrustedApplicationCopyData(app, &data);
+ if (status)
+ {
+ cssmPerror("SecTrustedApplicationCopyData", status);
+ continue;
+ }
+
+ bytes = CFDataGetBytePtr(data);
+ if (bytes && bytes[0] == 0x2f) {
+ fprintf(stream, "%s", (const char *)bytes);
+ if ((status = SecTrustedApplicationValidateWithPath(app, (const char *)bytes)) == noErr) {
+ fprintf(stream, " (OK)");
+ } else {
+ fprintf(stream, " (status %ld)", status);
+ }
+ fprintf(stream, "\n");
+ } else {
+ print_cfdata(stream, data);
+ fputc('\n', stream);
+ }
+ if (data)
+ CFRelease(data);
+ }
+
+
+ if (interactive)
+ {
+ char buffer[10] = {};
+ if(applicationList != NULL) {
+ fprintf(stderr, "NULL out this application list? ");
+ if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
+ {
+ /*
+ * This makes the ops in this entry wide-open, no dialog or confirmation
+ * other than requiring the keychain be open.
+ */
+ fprintf(stderr, "setting app list to NULL\n");
+ status = SecACLSetSimpleContents(acl, NULL, description, &promptSelector);
+ if (status)
+ {
+ cssmPerror("SecACLSetSimpleContents", status);
+ continue;
+ }
+ }
+ else {
+ fprintf(stderr, "Set this application list to empty array? ");
+ if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
+ {
+ /*
+ * This means "always get confirmation, from all apps".
+ */
+ fprintf(stderr, "setting app list to empty array\n");
+ status = SecACLSetSimpleContents(acl,
+ CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks),
+ description, &promptSelector);
+ if (status)
+ {
+ cssmPerror("SecACLSetSimpleContents", status);
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ fprintf(stderr, "Remove this acl? ");
+ if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y')
+ {
+ /*
+ * This make ths ops in this entry completely inaccessible.
+ */
+ fprintf(stderr, "removing acl\n");
+ status = SecACLRemove(acl);
+ if (status)
+ {
+ cssmPerror("SecACLRemove", status);
+ continue;
+ }
+ }
+ }
+ }
+ if (description)
+ CFRelease(description);
+ if (applicationList)
+ CFRelease(applicationList);
+
+ }
+
+loser:
+ if (aclList)
+ CFRelease(aclList);
+
+ return result;
+}
+
+/* Simluate what StickyRecord is trying to do.... */
+
+/*
+ * Given an Access object:
+ * -- extract the ACL for the specified CSSM_ACL_AUTHORIZATION_TAG. We expect there
+ * to exactly one of these - if the form of a default ACL changes we'll have to
+ * revisit this.
+ * -- set the ACL's app list to the provided CFArray, which may be NULL (meaning
+ * "any app can access this, no problem"), an empty array (meaning "always
+ * prompt"), or an actual app list.
+ * -- set or clear the PROMPT_REQUIRE_PASSPHRASE bit per the requirePassphrase
+ * argument
+ */
+static OSStatus srUpdateAcl(
+ SecAccessRef accessRef,
+ CSSM_ACL_AUTHORIZATION_TAG whichAcl, // e.g. CSSM_ACL_AUTHORIZATION_DECRYPT
+ CFArrayRef appArray,
+ bool requirePassphrase)
+{
+ OSStatus ortn;
+ CFArrayRef aclList = NULL;
+
+ ortn = SecAccessCopySelectedACLList(accessRef, whichAcl, &aclList);
+ if(ortn) {
+ cssmPerror("SecAccessCopySelectedACLList", ortn);
+ return ortn;
+ }
+
+ if(CFArrayGetCount(aclList) != 1) {
+ printf("StickyRecord::updateAcl - unexpected ACL list count (%d)",
+ (int)CFArrayGetCount(aclList));
+ return internalComponentErr;
+ }
+ SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
+
+ CFArrayRef applicationList = NULL;
+ CFStringRef description = NULL;
+ CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {};
+ ortn = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector);
+ if(ortn) {
+ cssmPerror("SecACLCopySimpleContents", ortn);
+ return ortn;
+ }
+ if(applicationList != NULL) {
+ CFRelease(applicationList);
+ }
+ if(requirePassphrase) {
+ promptSelector.flags |= CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE;
+ }
+ else {
+ promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE;
+ }
+ /* update */
+ ortn = SecACLSetSimpleContents(acl, appArray, description, &promptSelector);
+
+ /* we got this from SecACLCopySimpleContents - release it regardless */
+ if(description != NULL) {
+ CFRelease(description);
+ }
+ if(ortn) {
+ cssmPerror("SecACLSetSimpleContents", ortn);
+ }
+ if(aclList != NULL) {
+ CFRelease(aclList);
+ }
+ return ortn;
+}
+
+OSStatus stickyRecordUpdateAcl(
+ SecAccessRef accessRef)
+{
+ OSStatus ortn;
+
+ printf("...updating ACL to simulate a StickyRecord\n");
+
+ /* First: decrypt. Wide open (NULL app list), !REQUIRE_PASSPHRASE. */
+ ortn = srUpdateAcl(accessRef, CSSM_ACL_AUTHORIZATION_DECRYPT, NULL, false);
+ if(ortn) {
+ return ortn;
+ }
+
+ /* encrypt: always ask (empty app list, require passphrase */
+ CFArrayRef nullArray = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
+ return srUpdateAcl(accessRef, CSSM_ACL_AUTHORIZATION_ENCRYPT, nullArray, true);
+
+}