X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTool/authz.c?ds=inline diff --git a/SecurityTool/authz.c b/SecurityTool/authz.c new file mode 100644 index 00000000..b7bd1ec8 --- /dev/null +++ b/SecurityTool/authz.c @@ -0,0 +1,723 @@ +/* + * Copyright (c) 2003-2004,2006-2007,2009-2010,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@ + * + * authz.c + */ + +#include +#include +#include + +#include "authz.h" +#include "security.h" + +// AEWP? + +AuthorizationRef +read_auth_ref_from_stdin() +{ + AuthorizationRef auth_ref = NULL; + AuthorizationExternalForm extform; + size_t bytes_read; + + while (kAuthorizationExternalFormLength != (bytes_read = read(STDIN_FILENO, &extform, kAuthorizationExternalFormLength))) + { + if ((bytes_read == -1) && ((errno != EAGAIN) || (errno != EINTR))) + break; + } + if (bytes_read != kAuthorizationExternalFormLength) + fprintf(stderr, "ERR: Failed to read authref\n"); + else + if (AuthorizationCreateFromExternalForm(&extform, &auth_ref)) + fprintf(stderr, "ERR: Failed to internalize authref\n"); + + close(0); + + return auth_ref; +} + +int +write_auth_ref_to_stdout(AuthorizationRef auth_ref) +{ + AuthorizationExternalForm extform; + size_t bytes_written; + + if (AuthorizationMakeExternalForm(auth_ref, &extform)) + return -1; + + while (kAuthorizationExternalFormLength != (bytes_written = write(STDOUT_FILENO, &extform, kAuthorizationExternalFormLength))) + { + if ((bytes_written == -1) && ((errno != EAGAIN) || (errno != EINTR))) + break; + } + + if (bytes_written == kAuthorizationExternalFormLength) + return 0; + + return -1; +} + +void +write_dict_to_stdout(CFDictionaryRef dict) +{ + if (!dict) + return; + + CFDataRef right_definition_xml = CFPropertyListCreateXMLData(NULL, dict); + + if (!right_definition_xml) + return; + + write(STDOUT_FILENO, CFDataGetBytePtr(right_definition_xml), CFDataGetLength(right_definition_xml)); + CFRelease(right_definition_xml); +} + +CFDictionaryRef +read_dict_from_stdin() +{ + int bytes_read = 0; + uint8_t buffer[4096]; + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); + CFErrorRef err = NULL; + + if (!data) + return NULL; + + while ((bytes_read = read(STDIN_FILENO, (void *)buffer, sizeof(buffer)))) + { + if (bytes_read == -1) + break; + else + CFDataAppendBytes(data, buffer, bytes_read); + } + + CFDictionaryRef right_dict = (CFDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, &err); + CFRelease(data); + + if (NULL == right_dict) { + CFShow(err); + return NULL; + } + + if (CFGetTypeID(right_dict) != CFDictionaryGetTypeID()) + { + fprintf(stderr, "This is not a dictionary.\n"); + CFRelease(right_dict); + return NULL; + } + return right_dict; +} + +CFPropertyListRef +read_plist_from_file(CFStringRef filePath) +{ + CFTypeRef property = NULL; + CFPropertyListRef propertyList = NULL; + CFURLRef fileURL = NULL; + CFErrorRef errorString = NULL; + CFDataRef resourceData = NULL; + Boolean status = FALSE; + SInt32 errorCode = -1; + + // Convert the path to a URL. + fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false); + if (NULL == fileURL) { + goto bail; + } + property = CFURLCreatePropertyFromResource(kCFAllocatorDefault, fileURL, kCFURLFileExists, NULL); + if (NULL == property) { + goto bail; + } + status = CFBooleanGetValue(property); + if (!status) { + fprintf(stderr, "The file does not exist.\n"); + goto bail; + } + + // Read the XML file. + status = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode); + if (!status) { + fprintf(stderr, "Error (%d) reading the file.\n", (int)errorCode); + goto bail; + } + + // Reconstitute the dictionary using the XML data. + propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, NULL, &errorString); + if (NULL == propertyList) { + CFShow(errorString); + goto bail; + } + + // Some error checking. + if (!CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0) || CFGetTypeID(propertyList) != CFDictionaryGetTypeID()) { + fprintf(stderr, "The file is invalid.\n"); + CFRelease(propertyList); + propertyList = NULL; + goto bail; + } + +bail: + if (NULL != fileURL) + CFRelease(fileURL); + if (NULL != property) + CFRelease(property); + if (NULL != resourceData) + CFRelease(resourceData); + + return propertyList; +} + +Boolean +write_plist_to_file(CFPropertyListRef propertyList, CFStringRef filePath) +{ + CFTypeRef property = NULL; + CFURLRef fileURL = NULL; + CFDataRef xmlData = NULL; + Boolean status = FALSE; + SInt32 errorCode = -1; + CFErrorRef errorRef = NULL; + + // Convert the path to a URL. + fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false); + if (NULL == fileURL) { + goto bail; + } + property = CFURLCreatePropertyFromResource(kCFAllocatorDefault, fileURL, kCFURLFileExists, NULL); + if (NULL == property) { + goto bail; + } + if (!CFBooleanGetValue(property)) { + fprintf(stderr, "The file does not exist.\n"); + goto bail; + } + + // Convert the property list into XML data. + xmlData = CFPropertyListCreateData(kCFAllocatorDefault, propertyList, kCFPropertyListXMLFormat_v1_0, 0, &errorRef); + if (errorRef) { + fprintf(stderr, "The file could not be written.\n"); + goto bail; + } + + // Write the XML data to the file. + if (!CFURLWriteDataAndPropertiesToResource(fileURL, xmlData, NULL, &errorCode)) { + fprintf(stderr, "The file could not be written.\n"); + goto bail; + } + + status = TRUE; +bail: + if (NULL != xmlData) + CFRelease(xmlData); + if (NULL != fileURL) + CFRelease(fileURL); + + return status; +} + +static void merge_dictionaries(const void *key, const void *value, void *mergeDict) +{ + CFDictionarySetValue(mergeDict, key, value); +} + +int +authorizationdb(int argc, char * const * argv) +{ + AuthorizationRef auth_ref = NULL; + int ch; + while ((ch = getopt(argc, argv, "i")) != -1) + { + switch (ch) + { + case 'i': + auth_ref = read_auth_ref_from_stdin(); + break; + case '?': + default: + return 2; + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + return 2; // required right parameter(s) + + OSStatus status; + + if (argc > 1) + { + if (!auth_ref && AuthorizationCreate(NULL, NULL, 0, &auth_ref)) + return -1; + + if (!strcmp("read", argv[0])) + { + CFDictionaryRef right_definition; + status = AuthorizationRightGet(argv[1], &right_definition); + if (!status) + { + write_dict_to_stdout(right_definition); + CFRelease(right_definition); + } + } + else if (!strcmp("write", argv[0])) + { + if (argc == 2) + { + CFDictionaryRef right_definition = read_dict_from_stdin(); + if (!right_definition) + return -1; + status = AuthorizationRightSet(auth_ref, argv[1], right_definition, NULL, NULL, NULL); + CFRelease(right_definition); + } + else if (argc == 3) + { + // argv[2] is shortcut string + CFStringRef shortcut_definition = CFStringCreateWithCStringNoCopy(NULL, argv[2], kCFStringEncodingUTF8, kCFAllocatorNull); + if (!shortcut_definition) + return -1; + status = AuthorizationRightSet(auth_ref, argv[1], shortcut_definition, NULL, NULL, NULL); + CFRelease(shortcut_definition); + } + else + return 2; // take one optional argument - no more + + } + else if (!strcmp("remove", argv[0])) + { + status = AuthorizationRightRemove(auth_ref, argv[1]); + } + else if (!strcmp("smartcard", argv[0])) + { + if (argc == 2) + { + if(!strcmp("status", argv[1])) + { + const CFStringRef SMARTCARD_LINE = CFSTR("builtin:smartcard-sniffer,privileged"); + const CFStringRef MECHANISMS = CFSTR("mechanisms"); + const CFStringRef BUILTIN_LINE = CFSTR("builtin:policy-banner"); + const char* SYSTEM_LOGIN_CONSOLE = "system.login.console"; + const char* AUTHENTICATE = "authenticate"; + + CFIndex requiredLine1 = -1; + CFIndex requiredLine2 = -1; + + CFDictionaryRef right_definition; + status = AuthorizationRightGet(SYSTEM_LOGIN_CONSOLE, &right_definition); + if(!status) + { + CFArrayRef mechanisms; + + Boolean res = CFDictionaryGetValueIfPresent(right_definition, MECHANISMS, (void*)&mechanisms); + if(res) + { + // now parse all array elements until "builtin:policy-banner" is found + CFIndex c = CFArrayGetCount(mechanisms); + CFStringRef mechanismName; + + for (CFIndex i = 0; i < c; ++i) + { + mechanismName = CFArrayGetValueAtIndex(mechanisms, i); + if(CFStringCompare(mechanismName, BUILTIN_LINE, 0) == kCFCompareEqualTo) + { + if(i + 1 < c) + { + mechanismName = CFArrayGetValueAtIndex(mechanisms, i + 1); + if(CFStringCompare(mechanismName, SMARTCARD_LINE, 0) == kCFCompareEqualTo) + { + requiredLine1 = i + 1; + } + break; + } + } + } + } + CFRelease(right_definition); + } + status = AuthorizationRightGet(AUTHENTICATE, &right_definition); + if(!status) + { + CFArrayRef mechanisms; + + Boolean res = CFDictionaryGetValueIfPresent(right_definition, MECHANISMS, (void*)&mechanisms); + if(res) + { + // now parse all array elements until "builtin:policy-banner" is found + CFIndex c = CFArrayGetCount(mechanisms); + CFStringRef mechanismName; + + if(c > 0) + { + mechanismName = CFArrayGetValueAtIndex(mechanisms, 0); + if(CFStringCompare(mechanismName, SMARTCARD_LINE, 0) == kCFCompareEqualTo) + { + requiredLine2 = 0; + } + } + } + CFRelease(right_definition); + } + printf("Current smartcard login state: %s (system.login.console %s, authentication rule %s)\n", requiredLine1 != -1 && requiredLine2 != -1 ?"enabled":"disabled", requiredLine1 != -1 ? "enabled":"disabled", requiredLine2 != -1 ? "enabled":"disabled"); + + } + else if(!strcmp("disable", argv[1])) + status = AuthorizationEnableSmartCard(auth_ref, FALSE); + else if(!strcmp("enable", argv[1])) + status = AuthorizationEnableSmartCard(auth_ref, TRUE); + else + return 2; // unrecognized parameter + } + else + return 2; // required parameter missing + } + else if (!strcmp("merge", argv[0])) { + status = 1; + CFStringRef sourcePath = NULL; + CFStringRef destPath = NULL; + CFPropertyListRef sourcePlist = NULL; + CFPropertyListRef destPlist = NULL; + CFDictionaryRef sourceRights = NULL; + CFDictionaryRef sourceRules = NULL; + CFDictionaryRef destRights = NULL; + CFDictionaryRef destRules = NULL; + CFIndex rightsCount = 0; + CFIndex rulesCount = 0; + CFMutableDictionaryRef mergeRights = NULL; + CFMutableDictionaryRef mergeRules = NULL; + CFMutableDictionaryRef outDict = NULL; + + if (argc < 2 || argc > 3) + return 2; + + if (!strcmp("-", argv[1])) { + // Merging from . + sourcePlist = read_dict_from_stdin(); + } else { + sourcePath = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], kCFStringEncodingUTF8); + if (NULL == sourcePath) { + goto bail; + } + sourcePlist = read_plist_from_file(sourcePath); + } + if (NULL == sourcePlist) + goto bail; + if (argc == 2) { + // Merging to /etc/authorization. + destPath = CFStringCreateWithCString(kCFAllocatorDefault, "/etc/authorization", kCFStringEncodingUTF8); + } else { + destPath = CFStringCreateWithCString(kCFAllocatorDefault, argv[2], kCFStringEncodingUTF8); + } + if (NULL == destPath) { + goto bail; + } + destPlist = read_plist_from_file(destPath); + if (NULL == destPlist) + goto bail; + + sourceRights = CFDictionaryGetValue(sourcePlist, CFSTR("rights")); + sourceRules = CFDictionaryGetValue(sourcePlist, CFSTR("rules")); + destRights = CFDictionaryGetValue(destPlist, CFSTR("rights")); + destRules = CFDictionaryGetValue(destPlist, CFSTR("rules")); + if (sourceRights) + rightsCount += CFDictionaryGetCount(sourceRights); + if (destRights) + rightsCount += CFDictionaryGetCount(destRights); + mergeRights = CFDictionaryCreateMutable(NULL, rightsCount, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (NULL == mergeRights) { + goto bail; + } + if (sourceRules) + rulesCount += CFDictionaryGetCount(sourceRules); + if (destRules) + rulesCount += CFDictionaryGetCount(destRules); + mergeRules = CFDictionaryCreateMutable(NULL, rulesCount, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (NULL == mergeRules) { + goto bail; + } + + if (destRights) + CFDictionaryApplyFunction(destRights, merge_dictionaries, mergeRights); + if (destRules) + CFDictionaryApplyFunction(destRules, merge_dictionaries, mergeRules); + if (sourceRights) + CFDictionaryApplyFunction(sourceRights, merge_dictionaries, mergeRights); + if (sourceRules) + CFDictionaryApplyFunction(sourceRules, merge_dictionaries, mergeRules); + + outDict = CFDictionaryCreateMutable(NULL, 3, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (NULL == outDict) { + goto bail; + } + if (CFDictionaryContainsKey(sourcePlist, CFSTR("comment"))) + CFDictionaryAddValue(outDict, CFSTR("comment"), CFDictionaryGetValue(sourcePlist, CFSTR("comment"))); + else if (CFDictionaryContainsKey(destPlist, CFSTR("comment"))) + CFDictionaryAddValue(outDict, CFSTR("comment"), CFDictionaryGetValue(destPlist, CFSTR("comment"))); + CFDictionaryAddValue(outDict, CFSTR("rights"), mergeRights); + CFDictionaryAddValue(outDict, CFSTR("rules"), mergeRules); + if (!write_plist_to_file(outDict, destPath)) + goto bail; + + status = noErr; +bail: + if (sourcePath) + CFRelease(sourcePath); + if (destPath) + CFRelease(destPath); + if (sourcePlist) + CFRelease(sourcePlist); + if (destPlist) + CFRelease(destPlist); + if (outDict) + CFRelease(outDict); + } + else + return 2; + } + else + return 2; + + if (auth_ref) + AuthorizationFree(auth_ref, 0); + + if (!do_quiet) + fprintf(stderr, "%s (%d)\n", status ? "NO" : "YES", (int)status); + + return (status ? -1 : 0); +} + +int +authorize(int argc, char * const *argv) +{ + int ch; + OSStatus status; + + Boolean user_interaction_allowed = FALSE, extend_rights = TRUE; + Boolean partial_rights = FALSE, destroy_rights = FALSE; + Boolean pre_authorize = FALSE, internalize = FALSE, externalize = FALSE; + Boolean wait = FALSE, explicit_credentials = FALSE; + Boolean isolate_explicit_credentials = FALSE, least_privileged = FALSE; + char *login = NULL; + + while ((ch = getopt(argc, argv, "ucC:EpdPieqwxl")) != -1) + { + switch (ch) + { + case 'u': + user_interaction_allowed = TRUE; + break; + case 'c': + explicit_credentials = TRUE; + break; + case 'C': + explicit_credentials = TRUE; + login = optarg; + break; + case 'e': + externalize = TRUE; + break; + case 'E': + extend_rights = FALSE; + break; + case 'p': + partial_rights = TRUE; + break; + case 'd': + destroy_rights = TRUE; + break; + case 'P': + pre_authorize = TRUE; + break; + case 'i': + internalize = TRUE; + break; + case 'w': + wait = TRUE; + externalize = TRUE; + break; + case 'x': + isolate_explicit_credentials = TRUE; + break; + case 'l': + least_privileged = TRUE; + break; + case '?': + default: + return 2; /* @@@ Return 2 triggers usage message. */ + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + return 2; // required right parameter(s) + +// set up AuthorizationFlags + AuthorizationFlags flags = kAuthorizationFlagDefaults | + (user_interaction_allowed ? kAuthorizationFlagInteractionAllowed : 0) | + (extend_rights ? kAuthorizationFlagExtendRights : 0) | + (partial_rights ? kAuthorizationFlagPartialRights : 0) | + (pre_authorize ? kAuthorizationFlagPreAuthorize : 0) | + (least_privileged ? kAuthorizationFlagLeastPrivileged : 0); + +// set up AuthorizationRightSet + AuthorizationItem rights[argc]; + memset(rights, '\0', argc * sizeof(AuthorizationItem)); + AuthorizationItemSet rightset = { argc, rights }; + while (argc > 0) + rights[--argc].name = *argv++; + + AuthorizationRef auth_ref = NULL; + +// internalize AuthorizationRef + if (internalize) + { + auth_ref = read_auth_ref_from_stdin(); + if (!auth_ref) + return 1; + } + + if (!auth_ref && AuthorizationCreate(NULL, NULL, + (least_privileged ? kAuthorizationFlagLeastPrivileged : 0), + &auth_ref)) + return -1; + +// prepare environment if needed + AuthorizationEnvironment *envp = NULL; + if (explicit_credentials) { + if (!login) + login = getlogin(); + char *pass = getpass("Password:"); + if (!(login && pass)) + return 1; + static AuthorizationItem authenv[] = { + { kAuthorizationEnvironmentUsername }, + { kAuthorizationEnvironmentPassword }, + { kAuthorizationEnvironmentShared } // optional (see below) + }; + static AuthorizationEnvironment env = { 0, authenv }; + authenv[0].valueLength = strlen(login); + authenv[0].value = login; + authenv[1].valueLength = strlen(pass); + authenv[1].value = pass; + if (isolate_explicit_credentials) + env.count = 2; // do not send kAuthorizationEnvironmentShared + else + env.count = 3; // do send kAuthorizationEnvironmentShared + envp = &env; + } + +// perform Authorization + AuthorizationRights *granted_rights = NULL; + status = AuthorizationCopyRights(auth_ref, &rightset, envp, flags, &granted_rights); + +// externalize AuthorizationRef + if (externalize) + write_auth_ref_to_stdout(auth_ref); + + if (!do_quiet) + fprintf(stderr, "%s (%d) ", status ? "NO" : "YES", (int)status); + + if (!do_quiet && !status && granted_rights) + { + uint32_t index; + fprintf(stderr, "{ %d: ", (int)granted_rights->count); + for (index = 0; index < granted_rights->count; index++) + { + fprintf(stderr, "\"%s\"%s %c ", granted_rights->items[index].name, + (kAuthorizationFlagCanNotPreAuthorize & granted_rights->items[index].flags) ? " (cannot-preauthorize)" : "", + (index+1 != granted_rights->count) ? ',' : '}'); + } + AuthorizationFreeItemSet(granted_rights); + } + + if (!do_quiet) + fprintf(stderr, "\n"); + +// wait for client to pick up AuthorizationRef + if (externalize && wait) + while (-1 != write(STDOUT_FILENO, NULL, 0)) + usleep(100); + +// drop AuthorizationRef + if (auth_ref) + AuthorizationFree(auth_ref, destroy_rights ? kAuthorizationFlagDestroyRights : 0); + + return (status ? -1 : 0); +} + + +int +execute_with_privileges(int argc, char * const *argv) +{ + AuthorizationRef auth_ref = NULL; + int ch; + while ((ch = getopt(argc, argv, "i")) != -1) + { + switch (ch) + { + case 'i': + auth_ref = read_auth_ref_from_stdin(); + break; + case '?': + default: + return 2; + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + return 2; // required tool parameter(s) + + OSStatus status; + + if (!auth_ref && AuthorizationCreate(NULL, NULL, 0, &auth_ref)) + return -1; + + FILE *communications_pipe = NULL; + + status = AuthorizationExecuteWithPrivileges(auth_ref,argv[0], 0, (argc > 1) ? &argv[1] : NULL, &communications_pipe); + + if (!do_quiet) + fprintf(stderr, "%s (%d) ", status ? "NO" : "YES", (int)status); + + if (!status) + { + int bytes_read = 0; + uint8_t buffer[4096]; + + while ((bytes_read = read(STDIN_FILENO, &buffer, sizeof(buffer)))) + { + if ((bytes_read == -1) && ((errno != EAGAIN) || (errno != EINTR))) + break; + else + while ((-1 == write(fileno(communications_pipe), buffer, bytes_read)) && + ((errno == EAGAIN) || (errno == EINTR))) + usleep(100); + } + } + + return (status ? -1 : 0); +} +