X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/cspxutils/acltool/aclUtils.cpp?ds=sidebyside diff --git a/SecurityTests/cspxutils/acltool/aclUtils.cpp b/SecurityTests/cspxutils/acltool/aclUtils.cpp new file mode 100644 index 00000000..5e0b727a --- /dev/null +++ b/SecurityTests/cspxutils/acltool/aclUtils.cpp @@ -0,0 +1,509 @@ +/* + * 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 +#include + +/* 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= ' ' && 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, ""); +} + +void +print_cfstring(FILE *stream, CFStringRef string) +{ + if (!string) + fprintf(stream, ""); + 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: \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); + +}