/* * Copyright (c) 2003-2004 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@ * * 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("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); }