#include <utilities/SecCFError.h>
#include <utilities/SecCFRelease.h>
#include <utilities/debugging.h>
#include <notify.h>
#include "keychain/SecureObjectSync/SOSInternal.h"
#include <Security/OTConstants.h>
enum {
parameterError = -50,
allocationError = -108,
};
bool SecKernError(kern_return_t result, CFErrorRef *error, CFStringRef format, ...) {
if (!result) return true;
if (error) {
va_list args;
CFIndex code = result;
CFErrorRef previousError = *error;
*error = NULL;
va_start(args, format);
SecCFCreateErrorWithFormatAndArguments(code, kSecKernDomain, previousError, error, NULL, format, args);
va_end(args);
}
return false;
}
bool SecCheckErrno(int result, CFErrorRef *error, CFStringRef format, ...) {
if (result == 0) return true;
if (error) {
va_list args;
int errnum = errno;
CFIndex code = errnum;
CFErrorRef previousError = *error;
*error = NULL;
va_start(args, format);
CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
va_end(args);
SecCFCreateErrorWithFormat(code, kSecErrnoDomain, previousError, error, NULL, CFSTR("%@: [%d] %s"), message, errnum, strerror(errnum));
CFReleaseSafe(message);
}
return false;
}
bool SecError(OSStatus status, CFErrorRef *error, CFStringRef format, ...) {
if (status == 0) {
return true;
}
CFErrorRef localError = NULL;
va_list args;
CFIndex code = status;
va_start(args, format);
SecCFCreateErrorWithFormatAndArguments(code, kSecErrorDomain, error ? *error : NULL, &localError, NULL, format, args);
va_end(args);
if (error) {
*error = localError; } else {
secdebug("secerror", "Error, but no out-parameter for error: %@", localError);
CFReleaseNull(localError);
}
return false;
}
bool SecRequirementError(bool requirement, CFErrorRef *error, CFStringRef format, ...) {
if (requirement) return true;
if (error) {
va_list args;
CFErrorRef previousError = *error;
*error = NULL;
va_start(args, format);
SecCFCreateErrorWithFormatAndArguments(parameterError, kSecErrorDomain, previousError, error, NULL, format, args);
va_end(args);
}
return false;
}
bool SecAllocationError(const void *allocated, CFErrorRef *error, CFStringRef format, ...) {
if (allocated) return true;
if (error) {
va_list args;
CFErrorRef previousError = *error;
*error = NULL;
va_start(args, format);
SecCFCreateErrorWithFormatAndArguments(allocationError, kSecErrorDomain, previousError, error, NULL, format, args);
va_end(args);
}
return false;
}
bool SecCFCreateErrorWithFormat(CFIndex errorCode, CFStringRef domain, CFErrorRef previousError, CFErrorRef *newError,
CFDictionaryRef formatoptions, CFStringRef format, ...)
{
va_list args;
va_start(args, format);
bool result = SecCFCreateErrorWithFormatAndArguments(errorCode, domain, previousError, newError, formatoptions, format, args);
va_end(args);
return result;
}
static bool SecCFErrorIsEqual(CFIndex errorCode, CFStringRef domain, CFStringRef description, CFErrorRef previousError)
{
bool isEqual = false;
bool equalDescriptions = false;
if (previousError == NULL) {
return false;
}
CFDictionaryRef previousUserInfo = CFErrorCopyUserInfo(previousError);
CFStringRef previousDescription = CFDictionaryGetValue(previousUserInfo, kCFErrorDescriptionKey);
if (previousDescription) {
equalDescriptions = CFStringCompare(description, previousDescription, 0) == kCFCompareEqualTo ? true : false;
}
CFReleaseNull(previousUserInfo);
bool equalCodes = errorCode == CFErrorGetCode(previousError);
CFErrorDomain previousDomain = CFErrorGetDomain(previousError);
bool equalDomains = CFStringCompare(domain, previousDomain, 0) == kCFCompareEqualTo ? true : false;
isEqual = equalCodes && equalDomains && equalDescriptions;
return isEqual;
}
#define CAP_LIMIT 200
static bool SecCFErrorShouldCapNestedError(CFErrorRef previousError, long *newCount)
{
bool shouldCap = false;
if (previousError) {
CFDictionaryRef userInfo = CFErrorCopyUserInfo(previousError);
if (userInfo && CFDictionaryContainsKey(userInfo, kSOSCountKey) == true) {
CFNumberRef previousCount = CFDictionaryGetValue(userInfo, kSOSCountKey);
if (previousCount) {
long previousLong = 0;
CFNumberGetValue(previousCount, kCFNumberLongType, &previousLong);
if (SecErrorIsNestedErrorCappingEnabled() && previousLong >= CAP_LIMIT) {
shouldCap = true;
} else {
*newCount = previousLong+1;
}
}
}
CFReleaseNull(userInfo);
} else {
*newCount = 0;
}
return shouldCap;
}
bool SecCFCreateErrorWithFormatAndArguments(CFIndex errorCode, CFStringRef domain,
CF_CONSUMED CFErrorRef previousError, CFErrorRef *newError,
CFDictionaryRef formatoptions, CFStringRef format, va_list args)
{
if (newError && !(*newError)) {
CFStringRef formattedString = CFStringCreateWithFormatAndArguments(NULL, formatoptions, format, args);
long newDepthCount = 0;
CFNumberRef newCount = NULL;
if (SecCFErrorIsEqual(errorCode, domain, formattedString, previousError) == true) {
secdebug("error_thee_well", "SecCFCreateErrorWithFormatAndArguments previous Error: %@ is equal to the new incoming error: domain: %@, error code: %ld, description: %@", previousError, domain, (long)errorCode, formattedString);
*newError = CFRetainSafe(previousError);
CFReleaseNull(previousError);
CFReleaseNull(formattedString);
return false;
} else if (SecCFErrorShouldCapNestedError(previousError, &newDepthCount) == true) {
secdebug("error_thee_well", "SecCFCreateErrorWithFormatAndArguments reached nested error limit, returning previous error: %@", previousError);
*newError = CFRetainSafe(previousError);
CFReleaseNull(previousError);
CFReleaseNull(formattedString);
return false;
} else {
newCount = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &newDepthCount);
}
CFMutableDictionaryRef newUserInfo = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
if (previousError) {
CFDictionaryAddValue(newUserInfo, kCFErrorUnderlyingErrorKey, previousError);
}
if (newCount) {
CFDictionaryAddValue(newUserInfo, kSOSCountKey, newCount);
}
if (formattedString) {
CFDictionaryAddValue(newUserInfo, kCFErrorDescriptionKey, formattedString);
}
*newError = CFErrorCreate(kCFAllocatorDefault, domain, errorCode, newUserInfo);
if (previousError) {
secdebug("error_thee_well", "encapsulated %@ with new error: %@", previousError, *newError);
}
CFReleaseNull(newCount);
CFReleaseNull(formattedString);
CFReleaseNull(newUserInfo);
CFReleaseNull(previousError);
} else {
if (previousError && newError && (previousError != *newError)) {
secdebug("error_thee_well", "dropping %@", previousError);
CFReleaseNull(previousError);
}
}
if (newError) {
secdebug("error_thee_well", "SecError: %@", *newError);
}
return false;
}