#include <getopt.h>
#include <stdio.h>
#include <Security/AuthorizationPriv.h>
#include <utilities/SecCFRelease.h>
#include "authz.h"
#include "security_tool.h"
static AuthorizationRef
read_auth_ref_from_stdin()
{
AuthorizationRef auth_ref = NULL;
AuthorizationExternalForm extform;
ssize_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;
}
static int
write_auth_ref_to_stdout(AuthorizationRef auth_ref)
{
AuthorizationExternalForm extform;
ssize_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;
}
static 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);
}
static CFDictionaryRef CF_RETURNS_RETAINED
read_dict_from_stdin()
{
ssize_t 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;
}
static CFPropertyListRef CF_RETURNS_RETAINED
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;
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;
}
status = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode);
if (!status) {
fprintf(stderr, "Error (%d) reading the file.\n", (int)errorCode);
goto bail;
}
propertyList = CFPropertyListCreateWithData(kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, NULL, &errorString);
if (NULL == propertyList) {
CFShow(errorString);
goto bail;
}
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;
}
static 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;
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;
}
xmlData = CFPropertyListCreateData(kCFAllocatorDefault, propertyList, kCFPropertyListXMLFormat_v1_0, 0, &errorRef);
if (errorRef) {
fprintf(stderr, "The file could not be written.\n");
goto bail;
}
if (!CFURLWriteDataAndPropertiesToResource(fileURL, xmlData, NULL, &errorCode)) {
fprintf(stderr, "The file could not be written.\n");
goto bail;
}
status = TRUE;
bail:
CFReleaseNull(property);
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 SHOW_USAGE_MESSAGE;
}
}
argc -= optind;
argv += optind;
if (argc == 0)
return SHOW_USAGE_MESSAGE;
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)
{
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 SHOW_USAGE_MESSAGE;
}
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)
{
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)
{
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 SHOW_USAGE_MESSAGE; }
else
return SHOW_USAGE_MESSAGE; }
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 SHOW_USAGE_MESSAGE;
if (!strcmp("-", argv[1])) {
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) {
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 SHOW_USAGE_MESSAGE;
}
else
return SHOW_USAGE_MESSAGE;
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;
int retval = 1;
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 SHOW_USAGE_MESSAGE;
}
}
argc -= optind;
argv += optind;
if (argc == 0)
return SHOW_USAGE_MESSAGE;
AuthorizationFlags flags = kAuthorizationFlagDefaults |
(user_interaction_allowed ? kAuthorizationFlagInteractionAllowed : 0) |
(extend_rights ? kAuthorizationFlagExtendRights : 0) |
(partial_rights ? kAuthorizationFlagPartialRights : 0) |
(pre_authorize ? kAuthorizationFlagPreAuthorize : 0) |
(least_privileged ? kAuthorizationFlagLeastPrivileged : 0);
AuthorizationItem *rights = malloc(argc * sizeof(AuthorizationItem));
if (!rights) {
fprintf(stderr, "Out of memory\n");
retval = 1;
goto bail;
}
memset(rights, '\0', argc * sizeof(AuthorizationItem));
AuthorizationItemSet rightset = { argc, rights };
while (argc > 0)
rights[--argc].name = *argv++;
AuthorizationRef auth_ref = NULL;
if (internalize)
{
auth_ref = read_auth_ref_from_stdin();
if (!auth_ref) {
retval = 1;
goto bail;
}
}
if (!auth_ref && AuthorizationCreate(NULL, NULL,
(least_privileged ? kAuthorizationFlagLeastPrivileged : 0),
&auth_ref)) {
retval = -1;
goto bail;
}
AuthorizationEnvironment *envp = NULL;
if (explicit_credentials) {
if (!login)
login = getlogin();
char *pass = getpass("Password:");
if (!(login && pass)) {
retval = 1;
goto bail;
}
static AuthorizationItem authenv[] = {
{ kAuthorizationEnvironmentUsername },
{ kAuthorizationEnvironmentPassword },
{ kAuthorizationEnvironmentShared } };
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; else
env.count = 3; envp = &env;
}
AuthorizationRights *granted_rights = NULL;
status = AuthorizationCopyRights(auth_ref, &rightset, envp, flags, &granted_rights);
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");
if (externalize && wait)
while (-1 != write(STDOUT_FILENO, NULL, 0))
usleep(100);
if (auth_ref)
AuthorizationFree(auth_ref, destroy_rights ? kAuthorizationFlagDestroyRights : 0);
retval = (status ? -1 : 0);
bail:
if (rights)
free(rights);
return retval;
}
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 SHOW_USAGE_MESSAGE;
}
}
argc -= optind;
argv += optind;
if (argc == 0)
return SHOW_USAGE_MESSAGE;
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)
{
ssize_t 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);
}