SecCFError.c
1 /* 2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25 #include <utilities/SecCFError.h> 26 #include <utilities/SecCFRelease.h> 27 #include <utilities/debugging.h> 28 #include <notify.h> 29 #include "keychain/SecureObjectSync/SOSInternal.h" 30 #include <Security/OTConstants.h> 31 32 // 33 // OSStatus values we magically know 34 // 35 enum { 36 parameterError = -50, /* One or more parameters passed to a function were not valid. */ 37 allocationError = -108, /* Failed to allocate memory. */ 38 }; 39 40 bool SecKernError(kern_return_t result, CFErrorRef *error, CFStringRef format, ...) { 41 if (!result) return true; 42 if (error) { 43 va_list args; 44 CFIndex code = result; 45 CFErrorRef previousError = *error; 46 47 *error = NULL; 48 va_start(args, format); 49 SecCFCreateErrorWithFormatAndArguments(code, kSecKernDomain, previousError, error, NULL, format, args); 50 va_end(args); 51 } 52 return false; 53 } 54 55 bool SecCheckErrno(int result, CFErrorRef *error, CFStringRef format, ...) { 56 if (result == 0) return true; 57 if (error) { 58 va_list args; 59 int errnum = errno; 60 CFIndex code = errnum; 61 CFErrorRef previousError = *error; 62 63 *error = NULL; 64 va_start(args, format); 65 CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args); 66 va_end(args); 67 SecCFCreateErrorWithFormat(code, kSecErrnoDomain, previousError, error, NULL, CFSTR("%@: [%d] %s"), message, errnum, strerror(errnum)); 68 CFReleaseSafe(message); 69 } 70 return false; 71 } 72 73 bool SecError(OSStatus status, CFErrorRef *error, CFStringRef format, ...) { 74 if (status == 0) { 75 return true; 76 } 77 78 CFErrorRef localError = NULL; 79 va_list args; 80 CFIndex code = status; 81 va_start(args, format); 82 SecCFCreateErrorWithFormatAndArguments(code, kSecErrorDomain, error ? *error : NULL, &localError, NULL, format, args); 83 va_end(args); 84 85 if (error) { 86 *error = localError; // Existing *error is consumed by SecCFCreateErrorWithFormatAndArguments 87 } else { 88 // This happens a bunch in our codebase, so this log can only really exist in debug builds 89 secdebug("secerror", "Error, but no out-parameter for error: %@", localError); 90 CFReleaseNull(localError); 91 } 92 93 return false; 94 } 95 96 // Parameter error 97 bool SecRequirementError(bool requirement, CFErrorRef *error, CFStringRef format, ...) { 98 if (requirement) return true; 99 if (error) { 100 va_list args; 101 CFErrorRef previousError = *error; 102 103 *error = NULL; 104 va_start(args, format); 105 SecCFCreateErrorWithFormatAndArguments(parameterError, kSecErrorDomain, previousError, error, NULL, format, args); 106 va_end(args); 107 } 108 return false; 109 } 110 111 // Allocation failure 112 bool SecAllocationError(const void *allocated, CFErrorRef *error, CFStringRef format, ...) { 113 if (allocated) return true; 114 if (error) { 115 va_list args; 116 CFErrorRef previousError = *error; 117 118 *error = NULL; 119 va_start(args, format); 120 SecCFCreateErrorWithFormatAndArguments(allocationError, kSecErrorDomain, previousError, error, NULL, format, args); 121 va_end(args); 122 } 123 return false; 124 } 125 126 bool SecCFCreateErrorWithFormat(CFIndex errorCode, CFStringRef domain, CFErrorRef previousError, CFErrorRef *newError, 127 CFDictionaryRef formatoptions, CFStringRef format, ...) 128 { 129 va_list args; 130 va_start(args, format); 131 132 bool result = SecCFCreateErrorWithFormatAndArguments(errorCode, domain, previousError, newError, formatoptions, format, args); 133 134 va_end(args); 135 136 return result; 137 } 138 139 static bool SecCFErrorIsEqual(CFIndex errorCode, CFStringRef domain, CFStringRef description, CFErrorRef previousError) 140 { 141 bool isEqual = false; 142 bool equalDescriptions = false; 143 144 if (previousError == NULL) { 145 return false; 146 } 147 148 CFDictionaryRef previousUserInfo = CFErrorCopyUserInfo(previousError); 149 CFStringRef previousDescription = CFDictionaryGetValue(previousUserInfo, kCFErrorDescriptionKey); 150 if (previousDescription) { 151 equalDescriptions = CFStringCompare(description, previousDescription, 0) == kCFCompareEqualTo ? true : false; 152 } 153 154 CFReleaseNull(previousUserInfo); 155 bool equalCodes = errorCode == CFErrorGetCode(previousError); 156 157 CFErrorDomain previousDomain = CFErrorGetDomain(previousError); 158 bool equalDomains = CFStringCompare(domain, previousDomain, 0) == kCFCompareEqualTo ? true : false; 159 160 isEqual = equalCodes && equalDomains && equalDescriptions; 161 return isEqual; 162 } 163 164 #define CAP_LIMIT 200 165 static bool SecCFErrorShouldCapNestedError(CFErrorRef previousError, long *newCount) 166 { 167 bool shouldCap = false; 168 169 if (previousError) { 170 CFDictionaryRef userInfo = CFErrorCopyUserInfo(previousError); 171 if (userInfo && CFDictionaryContainsKey(userInfo, kSOSCountKey) == true) { 172 CFNumberRef previousCount = CFDictionaryGetValue(userInfo, kSOSCountKey); 173 if (previousCount) { 174 long previousLong = 0; 175 CFNumberGetValue(previousCount, kCFNumberLongType, &previousLong); 176 if (SecErrorIsNestedErrorCappingEnabled() && previousLong >= CAP_LIMIT) { 177 shouldCap = true; 178 } else { 179 *newCount = previousLong+1; 180 } 181 } 182 } 183 CFReleaseNull(userInfo); 184 } else { 185 *newCount = 0; 186 } 187 return shouldCap; 188 } 189 190 // Also consumes whatever newError points to 191 bool SecCFCreateErrorWithFormatAndArguments(CFIndex errorCode, CFStringRef domain, 192 CF_CONSUMED CFErrorRef previousError, CFErrorRef *newError, 193 CFDictionaryRef formatoptions, CFStringRef format, va_list args) 194 { 195 if (newError && !(*newError)) { 196 CFStringRef formattedString = CFStringCreateWithFormatAndArguments(NULL, formatoptions, format, args); 197 198 long newDepthCount = 0; 199 CFNumberRef newCount = NULL; 200 201 if (SecCFErrorIsEqual(errorCode, domain, formattedString, previousError) == true) { 202 secdebug("error_thee_well", "SecCFCreateErrorWithFormatAndArguments previous Error: %@ is equal to the new incoming error: domain: %@, error code: %ld, description: %@", previousError, domain, (long)errorCode, formattedString); 203 *newError = CFRetainSafe(previousError); 204 CFReleaseNull(previousError); 205 CFReleaseNull(formattedString); 206 return false; 207 } else if (SecCFErrorShouldCapNestedError(previousError, &newDepthCount) == true) { 208 secdebug("error_thee_well", "SecCFCreateErrorWithFormatAndArguments reached nested error limit, returning previous error: %@", previousError); 209 *newError = CFRetainSafe(previousError); 210 CFReleaseNull(previousError); 211 CFReleaseNull(formattedString); 212 return false; 213 } else { 214 newCount = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &newDepthCount); 215 } 216 217 CFMutableDictionaryRef newUserInfo = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 218 219 if (previousError) { 220 CFDictionaryAddValue(newUserInfo, kCFErrorUnderlyingErrorKey, previousError); 221 } 222 if (newCount) { 223 CFDictionaryAddValue(newUserInfo, kSOSCountKey, newCount); 224 } 225 if (formattedString) { 226 CFDictionaryAddValue(newUserInfo, kCFErrorDescriptionKey, formattedString); 227 } 228 229 *newError = CFErrorCreate(kCFAllocatorDefault, domain, errorCode, newUserInfo); 230 231 if (previousError) { 232 secdebug("error_thee_well", "encapsulated %@ with new error: %@", previousError, *newError); 233 } 234 CFReleaseNull(newCount); 235 CFReleaseNull(formattedString); 236 CFReleaseNull(newUserInfo); 237 CFReleaseNull(previousError); 238 } else { 239 if (previousError && newError && (previousError != *newError)) { 240 secdebug("error_thee_well", "dropping %@", previousError); 241 CFReleaseNull(previousError); 242 } 243 } 244 245 if (newError) { 246 secdebug("error_thee_well", "SecError: %@", *newError); 247 } 248 249 return false; 250 }