]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/SecCFError.c
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / utilities / 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 }