/ OSX / utilities / SecCFError.c
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  }