Authorization.c   [plain text]


/* Copyright (c) 2012 Apple Inc. All rights reserved. */

#include "Authorization.h"
#include "authd_private.h"
#include "authutilities.h"
#include "debugging.h"

#include <Security/AuthorizationPriv.h>
#include <Security/AuthorizationDB.h>
#include <Security/AuthorizationTags.h>
#include <Security/AuthorizationTagsPriv.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
#include <mach/mach.h>
#include <syslog.h>
#include <AssertMacros.h>
#include <CoreFoundation/CFXPCBridge.h>

static dispatch_queue_t
get_authorization_dispatch_queue()
{
    static dispatch_once_t onceToken = 0;
    static dispatch_queue_t connection_queue = NULL;
 
    dispatch_once(&onceToken, ^{
        connection_queue = dispatch_queue_create("authorization-connection-queue", DISPATCH_QUEUE_SERIAL);
    });
    
    return connection_queue;
}

static xpc_connection_t
get_authorization_connection()
{
    static xpc_connection_t connection = NULL;
    
    dispatch_sync(get_authorization_dispatch_queue(), ^{
        if (connection == NULL) {
            connection = xpc_connection_create(SECURITY_AUTH_NAME, NULL);

            if (!connection) {
                syslog(LOG_ERR, "Authorization, failed to create xpc connection to %s", SECURITY_AUTH_NAME);
                connection = xpc_connection_create(SECURITY_AUTH_NAME, NULL);
            }
            
            xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
                if (xpc_get_type(event) == XPC_TYPE_ERROR) {
                    if (event == XPC_ERROR_CONNECTION_INVALID) {
                        syslog(LOG_ERR, "Authorization, server not available");
                    }
                    // XPC_ERROR_CONNECTION_INTERRUPTED
                    // XPC_ERROR_TERMINATION_IMMINENT
                } else {
                    char * desc = xpc_copy_description(event);
                    syslog(LOG_ERR, "Authorization, we should never get messages on this connection: %s", desc);
                    free(desc);
                }
            });
            
            xpc_connection_resume(connection);
            
            // Send
            xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
            xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_SETUP);
            mach_port_t bootstrap = MACH_PORT_NULL;
            task_get_bootstrap_port(mach_task_self(), &bootstrap);
            xpc_dictionary_set_mach_send(message, AUTH_XPC_BOOTSTRAP, bootstrap);
            xpc_object_t reply = xpc_connection_send_message_with_reply_sync(connection, message);
            xpc_release_safe(message);
            xpc_release_safe(reply);
        }
    });
    
    return connection;
}

static void
setItemSet(xpc_object_t message, const char * key, const AuthorizationItemSet * itemSet)
{
    xpc_object_t serialized = SerializeItemSet(itemSet);
    if (serialized) {
        xpc_dictionary_set_value(message, key, serialized);
        xpc_release(serialized);
    }
}

OSStatus AuthorizationCreate(const AuthorizationRights *rights,
                    const AuthorizationEnvironment *environment,
                    AuthorizationFlags flags,
                    AuthorizationRef *authorization)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;

//    require_action(!(rights == NULL && authorization == NULL), done, status = errAuthorizationInvalidSet);
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_CREATE);
    setItemSet(message, AUTH_XPC_RIGHTS, rights);
    setItemSet(message, AUTH_XPC_ENVIROMENT, environment);
    xpc_dictionary_set_uint64(message, AUTH_XPC_FLAGS, flags | (authorization ? 0 : kAuthorizationFlagNoData));
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);

    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
    // Out
    if (authorization && status == errAuthorizationSuccess) {
        size_t len;
        const void * data = xpc_dictionary_get_data(reply, AUTH_XPC_BLOB, &len);
        require_action(data != NULL, done, status = errAuthorizationInternal);
        assert(len == sizeof(AuthorizationBlob));
        
        AuthorizationBlob * blob = (AuthorizationBlob*)calloc(1u, sizeof(AuthorizationBlob));
        require_action(blob != NULL, done, status = errAuthorizationInternal);
        *blob = *(AuthorizationBlob*)data;
        
        *authorization = (AuthorizationRef)blob;
    }

done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationCreateWithAuditToken(audit_token_t token,
                                 const AuthorizationEnvironment *environment,
                                 AuthorizationFlags flags,
                                 AuthorizationRef *authorization)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    
    require_action(authorization != NULL, done, status = errAuthorizationInvalidPointer);
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_CREATE_WITH_AUDIT_TOKEN);
    xpc_dictionary_set_data(message, AUTH_XPC_DATA, &token, sizeof(token));
    setItemSet(message, AUTH_XPC_ENVIROMENT, environment);
    xpc_dictionary_set_uint64(message, AUTH_XPC_FLAGS, flags);
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
    // Out
    if (status == errAuthorizationSuccess) {
        size_t len;
        const void * data = xpc_dictionary_get_data(reply, AUTH_XPC_BLOB, &len);
        require_action(data != NULL, done, status = errAuthorizationInternal);
        assert(len == sizeof(AuthorizationBlob));
        
        AuthorizationBlob * blob = (AuthorizationBlob*)calloc(1u, sizeof(AuthorizationBlob));
        require_action(blob != NULL, done, status = errAuthorizationInternal);
        *blob = *(AuthorizationBlob*)data;
        
        *authorization = (AuthorizationRef)blob;
    }
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationFree(AuthorizationRef authorization, AuthorizationFlags flags)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    AuthorizationBlob *blob = NULL;

    require_action(authorization != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authorization;
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_FREE);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    xpc_dictionary_set_uint64(message, AUTH_XPC_FLAGS, flags);

    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
    // Free
    free(blob);
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

static OSStatus
_AuthorizationCopyRights_send_message(xpc_object_t message, AuthorizationRights **authorizedRights)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t reply = NULL;

    // Send
    require_action(message != NULL, done, status = errAuthorizationInternal);

    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);

    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);

    // Out
    if (authorizedRights && status == errAuthorizationSuccess) {
        xpc_object_t tmpItems = xpc_dictionary_get_value(reply, AUTH_XPC_OUT_ITEMS);
        AuthorizationRights * grantedRights = DeserializeItemSet(tmpItems);
        require_action(grantedRights != NULL, done, status = errAuthorizationInternal);

        *authorizedRights = grantedRights;
    }

done:
    xpc_release_safe(reply);
    return status;
}

static OSStatus
_AuthorizationCopyRights_prepare_message(AuthorizationRef authorization, const AuthorizationRights *rights, const AuthorizationEnvironment *environment, AuthorizationFlags flags, xpc_object_t *message_out)
{
    OSStatus status = errAuthorizationInternal;
    AuthorizationBlob *blob = NULL;
    xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);

    require_action(authorization != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authorization;

    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_COPY_RIGHTS);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    setItemSet(message, AUTH_XPC_RIGHTS, rights);
    setItemSet(message, AUTH_XPC_ENVIROMENT, environment);
    xpc_dictionary_set_uint64(message, AUTH_XPC_FLAGS, flags);

    *message_out = message;
    message = NULL;
    status = errAuthorizationSuccess;

done:
    xpc_release_safe(message);
    return status;
}

OSStatus AuthorizationCopyRights(AuthorizationRef authorization,
                        const AuthorizationRights *rights,
                        const AuthorizationEnvironment *environment,
                        AuthorizationFlags flags,
                        AuthorizationRights **authorizedRights)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;

    require_noerr(status = _AuthorizationCopyRights_prepare_message(authorization, rights, environment, flags, &message), done);
    require_noerr(status = _AuthorizationCopyRights_send_message(message, authorizedRights), done);

done:
    xpc_release_safe(message);
    return status;
}


void AuthorizationCopyRightsAsync(AuthorizationRef authorization,
                         const AuthorizationRights *rights,
                         const AuthorizationEnvironment *environment,
                         AuthorizationFlags flags,
                         AuthorizationAsyncCallback callbackBlock)
{
    OSStatus prepare_status = errAuthorizationInternal;
    __block xpc_object_t message = NULL;

    prepare_status = _AuthorizationCopyRights_prepare_message(authorization, rights, environment, flags, &message);
    if (prepare_status != errAuthorizationSuccess) {
        callbackBlock(prepare_status, NULL);
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
		AuthorizationRights *blockAuthorizedRights = NULL;
        OSStatus status = _AuthorizationCopyRights_send_message(message, &blockAuthorizedRights);
        callbackBlock(status, blockAuthorizedRights);
        xpc_release_safe(message);
	});
}

OSStatus AuthorizationDismiss()
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;

    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require(message != NULL, done);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_DISMISS);
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationCopyInfo(AuthorizationRef authorization,
                      AuthorizationString tag,
                      AuthorizationItemSet **info)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    AuthorizationBlob *blob = NULL;
    
    require_action(info != NULL, done, status = errAuthorizationInvalidSet);
    require_action(authorization != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authorization;
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_COPY_INFO);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    if (tag) {
        xpc_dictionary_set_string(message, AUTH_XPC_TAG, tag);
    }
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);

    // Out
    if (info && status == errAuthorizationSuccess) {
        xpc_object_t tmpItems = xpc_dictionary_get_value(reply, AUTH_XPC_OUT_ITEMS);
        AuthorizationRights * outInfo = DeserializeItemSet(tmpItems);
        require_action(outInfo != NULL, done, status = errAuthorizationInternal);
        
        *info = outInfo;
    }
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationMakeExternalForm(AuthorizationRef authorization,
                              AuthorizationExternalForm *extForm)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    AuthorizationBlob *blob = NULL;
    
    require_action(extForm != NULL, done, status = errAuthorizationInvalidPointer);
    require_action(authorization != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authorization;
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_MAKE_EXTERNAL_FORM);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
    // out
    if (status == errAuthorizationSuccess) {
        size_t len;
        const void * data = xpc_dictionary_get_data(reply, AUTH_XPC_EXTERNAL, &len);
        require_action(data != NULL, done, status = errAuthorizationInternal);
        assert(len == sizeof(AuthorizationExternalForm));
        
        *extForm = *(AuthorizationExternalForm*)data;
    }
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationCreateFromExternalForm(const AuthorizationExternalForm *extForm,
                                    AuthorizationRef *authorization)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    
    require_action(extForm != NULL, done, status = errAuthorizationInvalidPointer);
    require_action(authorization != NULL, done, status = errAuthorizationInvalidRef);
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_CREATE_FROM_EXTERNAL_FORM);
    xpc_dictionary_set_data(message, AUTH_XPC_EXTERNAL, extForm, sizeof(AuthorizationExternalForm));
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
    // Out
    if (authorization && status == errAuthorizationSuccess) {
        size_t len;
        const void * data = xpc_dictionary_get_data(reply, AUTH_XPC_BLOB, &len);
        require_action(data != NULL, done, status = errAuthorizationInternal);
        assert(len == sizeof(AuthorizationBlob));
        
        AuthorizationBlob * blob = (AuthorizationBlob*)calloc(1u, sizeof(AuthorizationBlob));
        require_action(blob != NULL, done, status = errAuthorizationInternal);
        *blob = *(AuthorizationBlob*)data;
        
        *authorization = (AuthorizationRef)blob;
    }
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationFreeItemSet(AuthorizationItemSet *set)
{
    FreeItemSet(set);
    return errAuthorizationSuccess;
}

OSStatus AuthorizationEnableSmartCard(AuthorizationRef authRef, Boolean enable)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    AuthorizationBlob *blob = NULL;

    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    require_action(authRef != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authRef;
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_ENABLE_SMARTCARD);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    xpc_dictionary_set_bool(message, AUTH_XPC_DATA, enable);
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);

done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}


OSStatus AuthorizationRightGet(const char *rightName,
                      CFDictionaryRef *rightDefinition)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    
    require_action(rightName != NULL, done, status = errAuthorizationInvalidPointer);
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_RIGHT_GET);
    xpc_dictionary_set_string(message, AUTH_XPC_RIGHT_NAME, rightName);
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
    // Out
    if (rightDefinition && status == errAuthorizationSuccess) {
        xpc_object_t value = xpc_dictionary_get_value(reply, AUTH_XPC_DATA);
        require_action(value != NULL, done, status = errAuthorizationInternal);
        require_action(xpc_get_type(value) == XPC_TYPE_DICTIONARY, done, status = errAuthorizationInternal);
        
        CFTypeRef cfdict = _CFXPCCreateCFObjectFromXPCObject(value);
        require_action(cfdict != NULL, done, status = errAuthorizationInternal);
        
        *rightDefinition = cfdict;
    }
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationRightSet(AuthorizationRef authRef,
                      const char *rightName,
                      CFTypeRef rightDefinition,
                      CFStringRef descriptionKey,
                      CFBundleRef bundle,
                      CFStringRef tableName)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    AuthorizationBlob *blob = NULL;
    CFMutableDictionaryRef rightDict = NULL;
    CFBundleRef clientBundle = bundle;
    
    if (bundle) {
        CFRetain(bundle);
    }
    
    require_action(rightDefinition != NULL, done, status = errAuthorizationInvalidPointer);
    require_action(rightName != NULL, done, status = errAuthorizationInvalidPointer);
    require_action(authRef != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authRef;
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_RIGHT_SET);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    xpc_dictionary_set_string(message, AUTH_XPC_RIGHT_NAME, rightName);
    
    // Create rightDict
    if (CFGetTypeID(rightDefinition) == CFStringGetTypeID()) {
        rightDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        require_action(rightDict != NULL, done, status = errAuthorizationInternal);
        
        CFDictionarySetValue(rightDict, CFSTR(kAuthorizationRightRule), rightDefinition);
    
    } else if (CFGetTypeID(rightDefinition) == CFDictionaryGetTypeID()) {
        rightDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, rightDefinition);
        require_action(rightDict != NULL, done, status = errAuthorizationInternal);
    
    } else {
        status = errAuthorizationInvalidPointer;
        goto done;
    }
    
    // Create locDict
    if (descriptionKey) {
        CFMutableDictionaryRef locDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        require_action(locDict != NULL, done, status = errAuthorizationInternal);
        
        if (clientBundle == NULL) {
            clientBundle = CFBundleGetMainBundle();
            CFRetain(clientBundle);
        }
        
        if (clientBundle) {
            CFArrayRef bundleLocalizations = CFBundleCopyBundleLocalizations(clientBundle);
            if (bundleLocalizations) {
                // for every CFString in localizations do
                CFIndex locIndex, allLocs = CFArrayGetCount(bundleLocalizations);
                for (locIndex = 0; locIndex < allLocs; locIndex++)
                {
                    CFStringRef oneLocalization = (CFStringRef)CFArrayGetValueAtIndex(bundleLocalizations, locIndex);
                    
                    if (!oneLocalization)
                        continue;
                    
                    // @@@ no way to get "Localized" and "strings" as constants?
                    CFURLRef locURL = CFBundleCopyResourceURLForLocalization(clientBundle, tableName ? tableName :  CFSTR("Localizable"), CFSTR("strings"), NULL /*subDirName*/, oneLocalization);
                    
                    if (!locURL)
                        continue;
                    
                    CFDataRef tableData = NULL;
                    SInt32 errCode;
                    CFStringRef errStr = NULL;
                    CFPropertyListRef stringTable = NULL;
                    
                    CFURLCreateDataAndPropertiesFromResource(CFGetAllocator(clientBundle), locURL, &tableData, NULL, NULL, &errCode);
                    CFReleaseSafe(locURL);
                    if (errCode)
                    {
                        CFReleaseSafe(tableData);
                        continue;
                    }
                    
                    stringTable = CFPropertyListCreateFromXMLData(CFGetAllocator(clientBundle), tableData, kCFPropertyListImmutable, &errStr);
                    CFReleaseSafe(errStr);
                    CFReleaseSafe(tableData);
                    
                    CFStringRef value = (CFStringRef)CFDictionaryGetValue(stringTable, descriptionKey);
                    if (value == NULL || CFEqual(value, CFSTR(""))) {
                        CFReleaseSafe(stringTable);
                        continue;
                    } else {
                        // oneLocalization/value into our dictionary 
                        CFDictionarySetValue(locDict, oneLocalization, value);
                        CFReleaseSafe(stringTable);
                    }
                }
                CFReleaseSafe(bundleLocalizations);
            }
        }
        
        // add the description as the default localization into the dictionary
		CFDictionarySetValue(locDict, CFSTR(""), descriptionKey);
		
		// stuff localization table into right definition
		CFDictionarySetValue(rightDict, CFSTR(kAuthorizationRuleParameterDefaultPrompt), locDict);
        CFReleaseSafe(locDict);
    }
    
    xpc_object_t value = _CFXPCCreateXPCObjectFromCFObject(rightDict);
    xpc_dictionary_set_value(message, AUTH_XPC_DATA, value);
    xpc_release_safe(value);
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
done:
    CFReleaseSafe(clientBundle);
    CFReleaseSafe(rightDict);
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}

OSStatus AuthorizationRightRemove(AuthorizationRef authorization,
                         const char *rightName)
{
    OSStatus status = errAuthorizationInternal;
    xpc_object_t message = NULL;
    xpc_object_t reply = NULL;
    AuthorizationBlob *blob = NULL;
    
    require_action(rightName != NULL, done, status = errAuthorizationInvalidPointer);
    require_action(authorization != NULL, done, status = errAuthorizationInvalidRef);
    blob = (AuthorizationBlob *)authorization;
    
    // Send
    message = xpc_dictionary_create(NULL, NULL, 0);
    require_action(message != NULL, done, status = errAuthorizationInternal);
    
    xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_RIGHT_REMOVE);
    xpc_dictionary_set_data(message, AUTH_XPC_BLOB, blob, sizeof(AuthorizationBlob));
    xpc_dictionary_set_string(message, AUTH_XPC_RIGHT_NAME, rightName);
    
    // Reply
    reply = xpc_connection_send_message_with_reply_sync(get_authorization_connection(), message);
    require_action(reply != NULL, done, status = errAuthorizationInternal);
    require_action(xpc_get_type(reply) != XPC_TYPE_ERROR, done, status = errAuthorizationInternal);
    
    // Status
    status = (OSStatus)xpc_dictionary_get_int64(reply, AUTH_XPC_STATUS);
    
done:
    xpc_release_safe(message);
    xpc_release_safe(reply);
    return status;
}