/ OSX / utilities / debugging.c
debugging.c
  1  /*
  2   * Copyright (c) 2006-2010,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  /*
 26   * debugging.c - non-trivial debug support
 27   */
 28  #include "utilities/debugging.h"
 29  #include "utilities/debugging_test.h"
 30  #include "utilities/SecCFWrappers.h"
 31  #include "utilities/SecFileLocations.h"
 32  #include <CoreFoundation/CFSet.h>
 33  #include <CoreFoundation/CFString.h>
 34  #include <CoreFoundation/CFPreferences.h>
 35  
 36  #include <dispatch/dispatch.h>
 37  
 38  #include <stdarg.h>
 39  #include <stdlib.h>
 40  #include <stdio.h>
 41  #include <string.h>
 42  #include <pthread.h>
 43  #include <asl.h>
 44  #include <os/trace.h>
 45  #include <os/log_private.h>
 46  #include <sqlite3.h>
 47  #include <os/lock_private.h>
 48  
 49  const char *api_trace = "api_trace";
 50  
 51  
 52  const CFStringRef kStringNegate = CFSTR("-");
 53  const CFStringRef kStringAll = CFSTR("all");
 54  
 55  const CFStringRef kAPIScope = CFSTR("api");
 56  
 57  static CFMutableArrayRef sLogSettings = NULL; /* Either sets or dictionaries of level => set. */
 58  
 59  static dispatch_queue_t GetDispatchControlQueue(void) {
 60      static dispatch_queue_t sLoggingScopeControlQueue;
 61      static dispatch_once_t onceToken;
 62      dispatch_once(&onceToken, ^{
 63          sLoggingScopeControlQueue = dispatch_queue_create("security scope control", DISPATCH_QUEUE_CONCURRENT);
 64      });
 65      return sLoggingScopeControlQueue;
 66  }
 67  
 68  static void with_scopes_read(dispatch_block_t action) {
 69      dispatch_sync(GetDispatchControlQueue(), action);
 70  }
 71  
 72  static void with_scopes_write(dispatch_block_t action) {
 73      dispatch_barrier_sync(GetDispatchControlQueue(), action);
 74  }
 75  
 76  bool IsScopeActive(int level, CFStringRef scope)
 77  {
 78      if (scope == NULL)
 79          return true;
 80  
 81      CFNumberRef level_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level);
 82  
 83      __block bool isActive = false;
 84      with_scopes_read(^{
 85          if (sLogSettings) {
 86              CFArrayForEach(sLogSettings, ^(const void *value) {
 87                  CFSetRef setToCheck = NULL;
 88  
 89                  if (isSet(value)) {
 90                      setToCheck = (CFSetRef) value;
 91                  } else if (isDictionary(value)) {
 92                      CFDictionaryRef levels = (CFDictionaryRef) value;
 93  
 94                      setToCheck = CFDictionaryGetValue(levels, level_number);
 95  
 96                      if (!isSet(setToCheck))
 97                          setToCheck = NULL;
 98                  }
 99  
100                  if (setToCheck != NULL && !isActive) {
101                      bool negated = CFSetContainsValue(setToCheck, kStringNegate);
102                      bool inSet = CFSetContainsValue(setToCheck, scope);
103  
104                      isActive = negated ^ inSet;
105                  }
106              });
107          }
108      });
109  
110      CFReleaseNull(level_number);
111  
112      return isActive;
113  }
114  
115  bool IsScopeActiveC(int level, const char *scope)
116  {
117      CFStringRef scopeString = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)scope, strlen(scope), kCFStringEncodingUTF8, false);
118      bool isActive = IsScopeActive(level, scopeString);
119      CFReleaseNull(scopeString);
120  
121      return isActive;
122  }
123  
124  
125  static CFMutableSetRef CopyScopesFromScopeList(CFStringRef scopes) {
126      CFMutableSetRef resultSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
127  
128      CFStringRef allocated_scope_list = NULL;
129      CFStringRef clean_scope_list = scopes;
130      bool add_negate = false;
131  
132      if (CFStringHasPrefix(scopes, kStringNegate)) {
133          allocated_scope_list = CFStringCreateWithSubstring(kCFAllocatorDefault, scopes, CFRangeMake(CFStringGetLength(kStringNegate), CFStringGetLength(scopes) - 1));
134          clean_scope_list = allocated_scope_list;
135          add_negate = true;
136      }
137  
138      CFArrayRef commaArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, clean_scope_list, CFSTR(","));
139  
140      if (commaArray) {
141          CFArrayForEach(commaArray, ^(const void *value) {
142              if (isString(value)) {
143                  CFMutableStringRef copy = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) value);
144                  CFStringTrimWhitespace(copy);
145                  CFSetSetValue(resultSet, copy);
146                  CFReleaseNull(copy);
147              }
148          });
149      }
150  
151      CFSetRemoveValue(resultSet, CFSTR("none"));
152      CFSetRemoveValue(resultSet, CFSTR(""));
153  
154      if (CFSetContainsValue(resultSet, CFSTR("all"))) {
155          CFSetRemoveAllValues(resultSet);
156          add_negate = !add_negate;
157      }
158  
159      if (add_negate)
160          CFSetSetValue(resultSet, kStringNegate);
161  
162      CFReleaseNull(commaArray);
163      CFReleaseNull(allocated_scope_list);
164  
165      return resultSet;
166  }
167  
168  static CFMutableArrayRef CFArrayCreateMutableForCFTypesFilledWithCFNull(CFAllocatorRef allocator, CFIndex capacity) {
169      CFMutableArrayRef result = CFArrayCreateMutableForCFTypesWithCapacity(kCFAllocatorDefault, kScopeIDMax);
170  
171      for(int count = 0; count <= capacity; ++count)
172          CFArrayAppendValue(result, kCFNull);
173  
174      return result;
175  }
176  
177  static bool CFArrayIsAll(CFArrayRef array, const void *value)
178  {
179      return CFArrayGetCountOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), value) == CFArrayGetCount(array);
180  }
181  
182  static void SetNthScopeSet(int nth, CFTypeRef collection)
183  {
184      with_scopes_write(^{
185          if (sLogSettings == NULL) {
186              sLogSettings = CFArrayCreateMutableForCFTypesFilledWithCFNull(kCFAllocatorDefault, kScopeIDMax);
187          }
188  
189          CFArraySetValueAtIndex(sLogSettings, nth, collection);
190  
191          if (CFArrayIsAll(sLogSettings, kCFNull)) {
192              CFReleaseNull(sLogSettings);
193          }
194      });
195  }
196  
197  static int string_to_log_level(CFStringRef string) {
198      if (CFEqual(string, CFSTR(ASL_STRING_EMERG)))
199          return SECLOG_LEVEL_EMERG;
200      else if (CFEqual(string, CFSTR(ASL_STRING_ALERT)))
201          return SECLOG_LEVEL_ALERT;
202      else if (CFEqual(string, CFSTR(ASL_STRING_CRIT)))
203          return SECLOG_LEVEL_CRIT;
204      else if (CFEqual(string, CFSTR(ASL_STRING_ERR)))
205          return SECLOG_LEVEL_ERR;
206      else if (CFEqual(string, CFSTR(ASL_STRING_WARNING)))
207          return SECLOG_LEVEL_WARNING;
208      else if (CFEqual(string, CFSTR(ASL_STRING_NOTICE)))
209          return SECLOG_LEVEL_NOTICE;
210      else if (CFEqual(string, CFSTR(ASL_STRING_INFO)))
211          return SECLOG_LEVEL_INFO;
212      else if (CFEqual(string, CFSTR(ASL_STRING_DEBUG)))
213          return SECLOG_LEVEL_DEBUG;
214      else
215          return -1;
216  }
217  
218  static CFMutableArrayRef CFSetOfCFObjectsCopyValues(CFSetRef setOfCFs)
219  {
220      CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
221  
222      CFSetForEach(setOfCFs, ^(const void *value) {
223          CFArrayAppendValue(result, value);
224      });
225  
226      return result;
227  }
228  
229  CFPropertyListRef CopyCurrentScopePlist(void)
230  {
231      CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
232      with_scopes_read(^{
233          CFArrayForEach(sLogSettings, ^(const void *value) {
234              if (isSet(value)) {
235                  CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value);
236                  CFArrayAppendValue(result, values);
237                  CFReleaseNull(values);
238              } else if (isDictionary(value)) {
239                  CFMutableDictionaryRef levels = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
240  
241                  CFDictionaryForEach((CFDictionaryRef) value, ^(const void *key, const void *value) {
242                      if (isSet(value)) {
243                          CFArrayRef values = CFSetOfCFObjectsCopyValues((CFSetRef) value);
244                          CFDictionaryAddValue(levels, key, values);
245                          CFReleaseNull(values);
246                      }
247                  });
248  
249                  CFArrayAppendValue(result, levels);
250              } else {
251                  CFArrayAppendValue(result, kCFNull);
252              }
253          });
254      });
255      return result;
256  }
257  
258  void ApplyScopeDictionaryForID(CFDictionaryRef scopeDictionary, SecDebugScopeID whichID)
259  {
260      CFMutableDictionaryRef dictionary_for_id = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
261  
262      CFDictionaryForEach(scopeDictionary, ^(const void *key, const void *value) {
263          CFSetRef scope_set = NULL;
264          CFNumberRef key_number = NULL;
265          if (isString(key)) {
266              int level = string_to_log_level((CFStringRef) key);
267  
268              if (level >= 0)
269                  key_number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &level);
270          } else if (isNumber(key)) {
271              key_number = CFRetainSafe(key);
272          }
273  
274          if (isString(value)) {
275              scope_set = CopyScopesFromScopeList(value);
276          }
277  
278          if (key_number && scope_set)
279              CFDictionaryAddValue(dictionary_for_id, key_number, scope_set);
280  
281          CFReleaseNull(key_number);
282          CFReleaseNull(scope_set);
283      });
284  
285      if (CFDictionaryGetCount(dictionary_for_id) > 0) {
286          SetNthScopeSet(whichID, dictionary_for_id);
287      }
288  
289      CFReleaseNull(dictionary_for_id);
290  }
291  
292  void ApplyScopeListForID(CFStringRef scopeList, SecDebugScopeID whichID)
293  {
294      CFMutableSetRef scopesToUse = CopyScopesFromScopeList(scopeList);
295  
296      SetNthScopeSet(whichID, scopesToUse);
297  
298      CFReleaseNull(scopesToUse);
299  }
300  
301  void ApplyScopeListForIDC(const char *scopeList, SecDebugScopeID whichID) {
302      CFStringRef scope_string = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, scopeList, kCFStringEncodingUTF8, kCFAllocatorNull);
303  
304      ApplyScopeListForID(scope_string, whichID);
305  
306      CFReleaseNull(scope_string);
307  }
308  
309  #pragma mark - Log Handlers to catch log information
310  
311  
312  /*
313   * Instead of using CFPropertyListReadFromFile we use a
314   * CFPropertyListCreateWithStream directly
315   * here. CFPropertyListReadFromFile() uses
316   * CFURLCopyResourcePropertyForKey() andCF pulls in CoreServices for
317   * CFURLCopyResourcePropertyForKey() and that doesn't work in install
318   * environment.
319   */
320  
321  static CFPropertyListRef
322  CopyPlistFromFile(CFURLRef url)
323  {
324      CFDictionaryRef d = NULL;
325      CFReadStreamRef s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
326      if (s && CFReadStreamOpen(s)) {
327  	    d = (CFDictionaryRef)CFPropertyListCreateWithStream(kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, NULL);
328  	}
329      CFReleaseSafe(s);
330  
331      return d;
332  }
333  
334  static void ApplyScopeByTypeForID(CFPropertyListRef scopes, SecDebugScopeID whichID) {
335      if (isDictionary(scopes)) {
336          ApplyScopeDictionaryForID(scopes, whichID);
337      } else if (isString(scopes)) {
338          ApplyScopeListForID(scopes, whichID);
339      }
340  }
341  
342  static void setup_config_settings() {
343      CFStringRef logFileName;
344  #if TARGET_OS_IPHONE
345      logFileName = CFSTR(".GlobalPreferences.plist");
346  #else
347      logFileName = CFSTR("com.apple.security.logging.plist");
348  #endif
349      CFURLRef prefURL = SecCopyURLForFileInManagedPreferencesDirectory(logFileName);
350      if(prefURL) {
351          CFPropertyListRef plist = CopyPlistFromFile(prefURL);
352          if (plist) {
353              ApplyScopeByTypeForID(CFDictionaryGetValue(plist, CFSTR("SecLogging")), kScopeIDConfig);
354          }
355          CFReleaseSafe(plist);
356      }
357      CFReleaseSafe(prefURL);
358  }
359  
360  static void setup_defaults_settings() {
361      CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
362  
363      ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults);
364  
365      CFReleaseSafe(scopes_value);
366  }
367  
368  static void setup_circle_defaults_settings() {
369      CFPropertyListRef scopes_value = CFPreferencesCopyValue(CFSTR("Circle-Logging"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
370  
371      ApplyScopeByTypeForID(scopes_value, kScopeIDDefaults);
372  
373      CFReleaseSafe(scopes_value);
374  }
375  
376  static void setup_environment_scopes() {
377      const char *cur_scope = getenv("DEBUGSCOPE");
378      if (cur_scope == NULL)
379          cur_scope = "";
380  
381      ApplyScopeListForIDC(cur_scope, kScopeIDEnvironment);
382  }
383  
384  
385  void __security_debug_init(void) {
386      static dispatch_once_t sdOnceToken;
387  
388      dispatch_once(&sdOnceToken, ^{
389          setup_environment_scopes();
390          setup_config_settings();
391          setup_defaults_settings();
392          setup_circle_defaults_settings();
393      });
394  }
395  
396  
397  os_log_t
398  secLogObjForCFScope(CFStringRef scope)
399  {
400      os_log_t retval = OS_LOG_DISABLED;
401      static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
402      static CFMutableDictionaryRef scopeMap = NULL;
403  
404      if (scope == NULL) {
405          scope = CFSTR("logging");
406      }
407  
408      os_unfair_lock_lock_with_options(&lock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
409  
410      if (scopeMap == NULL) {
411          scopeMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL);
412      }
413  
414      retval = (os_log_t)CFDictionaryGetValue(scopeMap, scope);
415      if (retval == NULL) {
416          CFStringPerformWithCString(scope, ^(const char *scopeStr) {
417              CFDictionaryAddValue(scopeMap, scope, os_log_create("com.apple.securityd", scopeStr));
418          });
419          retval = (os_log_t)CFDictionaryGetValue(scopeMap, scope);
420      }
421  
422      os_unfair_lock_unlock(&lock);
423  
424      return retval;
425  }
426  
427  static bool loggingEnabled = true;
428  static pthread_mutex_t loggingMutex = PTHREAD_MUTEX_INITIALIZER;
429  
430  bool secLogEnabled(void) {
431      bool r = false;
432      pthread_mutex_lock(&loggingMutex);
433      r = loggingEnabled;
434      pthread_mutex_unlock(&loggingMutex);
435      return r;
436  }
437  void secLogDisable(void) {
438      pthread_mutex_lock(&loggingMutex);
439      loggingEnabled = false;
440      pthread_mutex_unlock(&loggingMutex);
441  }
442  
443  void secLogEnable(void) {
444      pthread_mutex_lock(&loggingMutex);
445      loggingEnabled = true;
446      pthread_mutex_unlock(&loggingMutex);
447  }
448  
449  os_log_t secLogObjForScope(const char *scope) {
450      if (!secLogEnabled())
451          return OS_LOG_DISABLED;
452      CFStringRef cfscope = NULL;
453      if(scope) cfscope =  CFStringCreateWithCString(kCFAllocatorDefault, scope, kCFStringEncodingASCII);
454      os_log_t retval = secLogObjForCFScope(cfscope);
455      CFReleaseNull(cfscope);
456      return retval;
457  }
458  
459  
460  
461  CFStringRef SecLogAPICreate(bool apiIn, const char *api, CFStringRef format, ... ) {
462      CFMutableStringRef outStr = CFStringCreateMutable(kCFAllocatorDefault, 0);
463  
464      char *direction = apiIn ? "ENTER" : "RETURN";
465      va_list args;
466      va_start(args, format);
467  
468      CFStringAppend(outStr, CFSTR("SecAPITrace "));
469      CFStringAppendCString(outStr, api, kCFStringEncodingASCII);
470      CFStringAppendCString(outStr, direction, kCFStringEncodingASCII);
471      
472      if (format) {
473          CFStringRef message = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, format, args);
474          CFStringAppend(outStr, message);
475          CFReleaseSafe(message);
476      }
477      
478      if (apiIn) {
479          char caller_info[80];
480          snprintf(caller_info, sizeof(caller_info), "C%p F%p", __builtin_return_address(1), __builtin_frame_address(2));
481          CFStringAppend(outStr, CFSTR("CALLER "));
482          CFStringAppendCString(outStr, caller_info, kCFStringEncodingASCII);
483      }
484      va_end(args);
485  
486      return outStr;
487  }