]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/src/SecCertificateTrace.c
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / utilities / src / SecCertificateTrace.c
1 /*
2 * Copyright (c) 2013-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 "SecCertificateTrace.h"
26 #include <TargetConditionals.h>
27 #include <inttypes.h>
28 #include "SecCFWrappers.h"
29 #include <sys/time.h>
30 #include <CoreFoundation/CoreFoundation.h>
31 #include "debugging.h"
32
33 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
34
35 SEC_CONST_DECL (kCertStatsPrefix, "com.apple.certstats");
36 SEC_CONST_DECL (kCertStatsCert, "cert");
37 SEC_CONST_DECL (kCertStatsPolicy, "id");
38 SEC_CONST_DECL (kCertStatsNotBefore, "nb");
39 SEC_CONST_DECL (kCertStatsNotAfter, "na");
40 SEC_CONST_DECL (kCertStatsSerialNumber, "sn");
41 SEC_CONST_DECL (kCertStatsSubjectSummary, "s");
42 SEC_CONST_DECL (kCertStatsIssuerSummary, "i");
43
44 static const CFStringRef kCertStatsFormat = CFSTR("%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@/%@=%@");
45 static const CFStringRef kCertStatsDelimiters = CFSTR("/");
46
47 bool SetCertificateTraceValueForKey(CFStringRef key, int64_t value);
48 void* BeginCertificateLoggingTransaction(void);
49 bool AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value);
50 void CloseCertificateLoggingTransaction(void* token);
51 CFStringRef SecCFStringCopyEncodingDelimiters(CFStringRef str, CFStringRef delimiters);
52
53 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
54 #include <asl.h>
55
56 static const char* gTopLevelKeyForCertificateTracing = "com.apple.certstats";
57
58 static const char* gMessageTracerSetPrefix = "com.apple.message.";
59
60 static const char* gMessageTracerDomainField = "com.apple.message.domain";
61
62 /* --------------------------------------------------------------------------
63 Function: OSX_BeginCertificateLoggingTransaction
64
65 Description: For OSX, the message tracer back end wants its logging
66 done in "bunches". This function allows for beginning
67 a 'transaction' of logging which will allow for putting
68 all of the transaction's items into a single log making
69 the message tracer folks happy.
70
71 The work of this function is to create the aslmsg context
72 and set the domain field and then return the aslmsg
73 context as a void* result.
74 -------------------------------------------------------------------------- */
75 static void* OSX_BeginCertificateLoggingTransaction()
76 {
77 void* result = NULL;
78 aslmsg mAsl = NULL;
79 mAsl = asl_new(ASL_TYPE_MSG);
80 if (NULL == mAsl)
81 {
82 return result;
83 }
84
85 asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForCertificateTracing);
86
87 result = (void *)mAsl;
88 return result;
89 }
90
91 /* --------------------------------------------------------------------------
92 Function: OSX_AddKeyValuePairToCertificateLoggingTransaction
93
94 Description: Once a call to OSX_BeginCertificateLoggingTransaction
95 is done, this call will allow for adding items to the
96 "bunch" of items being logged.
97
98 NOTE: The key should be a simple key such as
99 "numberOfPeers". This is because this function will
100 append the required prefix of "com.apple.message."
101 -------------------------------------------------------------------------- */
102 static bool OSX_AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
103 {
104 if (NULL == token || NULL == key)
105 {
106 return false;
107 }
108
109 aslmsg mAsl = (aslmsg)token;
110
111 // Fix up the key
112 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
113 if (NULL == real_key)
114 {
115 return false;
116 }
117
118 CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
119 key_length += 1; // For null
120 char key_buffer[key_length];
121 memset(key_buffer, 0,key_length);
122 if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
123 {
124 CFRelease(real_key);
125 return false;
126 }
127 CFRelease(real_key);
128
129 CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
130 if (NULL == value_str)
131 {
132 return false;
133 }
134
135 CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
136 value_str_numBytes += 1; // For null
137 char value_buffer[value_str_numBytes];
138 memset(value_buffer, 0, value_str_numBytes);
139 if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
140 {
141 CFRelease(value_str);
142 return false;
143 }
144 CFRelease(value_str);
145
146 asl_set(mAsl, key_buffer, value_buffer);
147 return true;
148 }
149
150 /* --------------------------------------------------------------------------
151 Function: OSX_CloseCertificateLoggingTransaction
152
153 Description: Once a call to OSX_BeginCertificateLoggingTransaction
154 is done, and all of the items that are to be in the
155 "bunch" of items being logged, this function will do the
156 real logging and free the aslmsg context.
157 -------------------------------------------------------------------------- */
158 static void OSX_CloseCertificateLoggingTransaction(void* token)
159 {
160 if (NULL != token)
161 {
162 aslmsg mAsl = (aslmsg)token;
163 asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
164 asl_free(mAsl);
165 }
166 }
167
168 /* --------------------------------------------------------------------------
169 Function: OSX_SetCertificateTraceValueForKey
170
171 Description: If "bunching" of items either cannot be done or is not
172 desired, then this 'single shot' function should be used.
173 It will create the aslmsg context, register the domain,
174 fix up the key and log the key value pair, and then
175 do the real logging and free the aslmsg context.
176 -------------------------------------------------------------------------- */
177 static bool OSX_SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
178 {
179 bool result = false;
180
181 if (NULL == key)
182 {
183 return result;
184 }
185
186 aslmsg mAsl = NULL;
187 mAsl = asl_new(ASL_TYPE_MSG);
188 if (NULL == mAsl)
189 {
190 return result;
191 }
192
193 // Fix up the key
194 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%@"), gMessageTracerSetPrefix, key);
195 if (NULL == real_key)
196 {
197 return false;
198 }
199
200 CFIndex key_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(real_key), kCFStringEncodingUTF8);
201 key_length += 1; // For null
202 char key_buffer[key_length];
203 memset(key_buffer, 0,key_length);
204 if (!CFStringGetCString(real_key, key_buffer, key_length, kCFStringEncodingUTF8))
205 {
206 CFRelease(real_key);
207 return false;
208 }
209 CFRelease(real_key);
210
211
212 CFStringRef value_str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lld"), value);
213 if (NULL == value_str)
214 {
215 asl_free(mAsl);
216 return result;
217 }
218
219 CFIndex value_str_numBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value_str), kCFStringEncodingUTF8);
220 value_str_numBytes += 1; // For null
221 char value_buffer[value_str_numBytes];
222 memset(value_buffer, 0, value_str_numBytes);
223 if (!CFStringGetCString(value_str, value_buffer, value_str_numBytes, kCFStringEncodingUTF8))
224 {
225 asl_free(mAsl);
226 CFRelease(value_str);
227 return result;
228 }
229 CFRelease(value_str);
230
231 /* Domain */
232 asl_set(mAsl, gMessageTracerDomainField, gTopLevelKeyForCertificateTracing);
233
234 /* Our custom key (starts with com.apple.message.) and its value */
235 asl_set(mAsl, key_buffer, value_buffer);
236
237 /* Log this ASL record (note actual log message is empty) */
238 asl_log(NULL, mAsl, ASL_LEVEL_NOTICE, "");
239 asl_free(mAsl);
240 return true;
241
242 }
243 #endif
244
245 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
246
247 static const char* gTopLevelKeyForCertificateTracing = "com.apple.certstats";
248
249 typedef void (*type_ADClientClearScalarKey)(CFStringRef key);
250 typedef void (*type_ADClientAddValueForScalarKey)(CFStringRef key, int64_t value);
251
252 static type_ADClientClearScalarKey gADClientClearScalarKey = NULL;
253 static type_ADClientAddValueForScalarKey gADClientAddValueForScalarKey = NULL;
254
255 static dispatch_once_t gADFunctionPointersSet = 0;
256 static CFBundleRef gAggdBundleRef = NULL;
257 static bool gFunctionPointersAreLoaded = false;
258
259 /* --------------------------------------------------------------------------
260 Function: InitializeADFunctionPointers
261
262 Description: Linking to the Aggregate library causes a build cycle,
263 so this function dynamically loads the needed function
264 pointers.
265 -------------------------------------------------------------------------- */
266 static bool InitializeADFunctionPointers()
267 {
268 if (gFunctionPointersAreLoaded)
269 {
270 return gFunctionPointersAreLoaded;
271 }
272
273 dispatch_once(&gADFunctionPointersSet,
274 ^{
275 CFStringRef path_to_aggd_framework = CFSTR("/System/Library/PrivateFrameworks/AggregateDictionary.framework");
276
277 CFURLRef aggd_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_to_aggd_framework, kCFURLPOSIXPathStyle, true);
278
279 if (NULL != aggd_url)
280 {
281 gAggdBundleRef = CFBundleCreate(kCFAllocatorDefault, aggd_url);
282 if (NULL != gAggdBundleRef)
283 {
284 gADClientClearScalarKey = (type_ADClientClearScalarKey)
285 CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientClearScalarKey"));
286
287 gADClientAddValueForScalarKey = (type_ADClientAddValueForScalarKey)
288 CFBundleGetFunctionPointerForName(gAggdBundleRef, CFSTR("ADClientAddValueForScalarKey"));
289 }
290 CFRelease(aggd_url);
291 }
292 });
293
294 gFunctionPointersAreLoaded = ((NULL != gADClientClearScalarKey) && (NULL != gADClientAddValueForScalarKey));
295 return gFunctionPointersAreLoaded;
296 }
297
298 /* --------------------------------------------------------------------------
299 Function: Internal_ADClientClearScalarKey
300
301 Description: This function is a wrapper around calling the
302 ADClientClearScalarKey function.
303
304 NOTE: The key should be a simple key such as
305 "numberOfPeers". This is because this function will
306 append the required prefix of "com.apple.certstats"
307 -------------------------------------------------------------------------- */
308 static void Internal_ADClientClearScalarKey(CFStringRef key)
309 {
310 if (InitializeADFunctionPointers())
311 {
312 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing, key);
313 if (NULL == real_key)
314 {
315 return;
316 }
317
318 gADClientClearScalarKey(real_key);
319 CFRelease(real_key);
320 }
321 }
322
323 /* --------------------------------------------------------------------------
324 Function: Internal_ADClientAddValueForScalarKey
325
326 Description: This function is a wrapper around calling the
327 ADClientAddValueForScalarKey function.
328
329 NOTE: The key should be a simple key such as
330 "numberOfPeers". This is because this function will
331 append the required prefix of "com.apple.certstats"
332 -------------------------------------------------------------------------- */
333 static void Internal_ADClientAddValueForScalarKey(CFStringRef key, int64_t value)
334 {
335 if (InitializeADFunctionPointers())
336 {
337 CFStringRef real_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%@"), gTopLevelKeyForCertificateTracing, key);
338 if (NULL == real_key)
339 {
340 return;
341 }
342
343 gADClientAddValueForScalarKey(real_key, value);
344 CFRelease(real_key);
345 }
346 }
347
348
349 /* --------------------------------------------------------------------------
350 Function: iOS_SetCertificateTraceValueForKey
351
352 Description: This function is a wrapper around calling either
353 ADClientAddValueForScalarKey or ADClientClearScalarKey,
354 depending on whether the value is 0.
355
356 NOTE: The key should be a simple key such as
357 "numberOfPeers". This is because this function will
358 append the required prefix of "com.apple.certstats"
359 -------------------------------------------------------------------------- */
360 static bool iOS_SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
361 {
362 if (NULL == key)
363 {
364 return false;
365 }
366
367 if (0LL == value)
368 {
369 Internal_ADClientClearScalarKey(key);
370 }
371 else
372 {
373 Internal_ADClientAddValueForScalarKey(key, value);
374 }
375 return true;
376 }
377
378 /* --------------------------------------------------------------------------
379 Function: iOS_AddKeyValuePairToCertificateLoggingTransaction
380
381 Description: For iOS there is no "bunching". This function will simply
382 call iOS_SetCloudKeychainTraceValueForKey to log the
383 key-value pair.
384 -------------------------------------------------------------------------- */
385 static bool iOS_AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
386 {
387 #pragma unused(token)
388 return iOS_SetCertificateTraceValueForKey(key, value);
389 }
390 #endif
391
392 /* --------------------------------------------------------------------------
393 Function: SetCertificateTraceValueForKey
394
395 Description: SPI to log a single key-value pair with the logging system
396 -------------------------------------------------------------------------- */
397 bool SetCertificateTraceValueForKey(CFStringRef key, int64_t value)
398 {
399 #if (TARGET_IPHONE_SIMULATOR)
400 return false;
401 #endif
402
403 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
404 return OSX_SetCertificateTraceValueForKey(key, value);
405 #endif
406
407 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
408 return iOS_SetCertificateTraceValueForKey(key, value);
409 #endif
410 }
411
412 /* --------------------------------------------------------------------------
413 Function: BeginCertificateLoggingTransaction
414
415 Description: SPI to begin a logging transaction
416 -------------------------------------------------------------------------- */
417 void* BeginCertificateLoggingTransaction(void)
418 {
419 #if (TARGET_IPHONE_SIMULATOR)
420 return (void *)-1;
421 #endif
422
423 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
424 return OSX_BeginCertificateLoggingTransaction();
425 #endif
426
427 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
428 return NULL;
429 #endif
430 }
431
432 /* --------------------------------------------------------------------------
433 Function: AddKeyValuePairToCertificateLoggingTransaction
434
435 Description: SPI to add a key-value pair to an outstanding logging
436 transaction
437 -------------------------------------------------------------------------- */
438 bool AddKeyValuePairToCertificateLoggingTransaction(void* token, CFStringRef key, int64_t value)
439 {
440 #if (TARGET_IPHONE_SIMULATOR)
441 return false;
442 #endif
443
444 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
445 return OSX_AddKeyValuePairToCertificateLoggingTransaction(token, key, value);
446 #endif
447
448 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
449 return iOS_AddKeyValuePairToCertificateLoggingTransaction(token, key, value);
450 #endif
451 }
452
453 /* --------------------------------------------------------------------------
454 Function: CloseCertificateLoggingTransaction
455
456 Description: SPI to complete a logging transaction and clean up the
457 context
458 -------------------------------------------------------------------------- */
459 void CloseCertificateLoggingTransaction(void* token)
460 {
461 #if (TARGET_IPHONE_SIMULATOR)
462 ; // nothing
463 #endif
464
465 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
466 OSX_CloseCertificateLoggingTransaction(token);
467 #endif
468
469 #if (TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR)
470 ; // nothing
471 #endif
472 }
473
474 /* --------------------------------------------------------------------------
475 Function: SecCFStringCopyEncodingDelimiters
476
477 Description: Utility to replace all characters in the given string
478 which match characters in the delimiters string. This
479 should be expanded later; for now we are assuming the
480 delimiters string is a single character, and we replace
481 it with an underscore instead of percent encoding.
482 -------------------------------------------------------------------------- */
483
484 CFStringRef SecCFStringCopyEncodingDelimiters(CFStringRef str, CFStringRef delimiters)
485 {
486 CFMutableStringRef newStr = (str) ? CFStringCreateMutableCopy(kCFAllocatorDefault, 0, str) : NULL;
487 if (newStr && delimiters)
488 {
489 CFStringRef replacementString = CFSTR("_");
490 CFIndex idx, count = CFStringGetLength(delimiters);
491 for (idx = 0; idx < count; idx++) {
492 UniChar uc = CFStringGetCharacterAtIndex(delimiters, idx);
493 CFStringRef stringToFind = CFStringCreateWithCharacters(kCFAllocatorDefault, &uc, 1);
494 if (stringToFind) {
495 CFStringFindAndReplace(newStr, stringToFind, replacementString, CFRangeMake(0, CFStringGetLength(newStr)), 0);
496 CFRelease(stringToFind);
497 }
498 }
499 }
500 return (CFStringRef) newStr;
501 }
502
503 /* --------------------------------------------------------------------------
504 Function: SecCertificateTraceAddRecord
505
506 Description: High-level SPI that logs a single certificate record,
507 given a dictionary containing key-value pairs.
508 -------------------------------------------------------------------------- */
509 bool SecCertificateTraceAddRecord(CFDictionaryRef certRecord)
510 {
511 bool result = false;
512 if (!certRecord)
513 return result;
514
515 /* encode delimiter characters in supplied strings */
516 CFStringRef policy = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsPolicy), kCertStatsDelimiters);
517 CFStringRef notBefore = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsNotBefore), kCertStatsDelimiters);
518 CFStringRef notAfter = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsNotAfter), kCertStatsDelimiters);
519 CFStringRef serial = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsSerialNumber), kCertStatsDelimiters);
520 CFStringRef subject = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsSubjectSummary), kCertStatsDelimiters);
521 CFStringRef issuer = SecCFStringCopyEncodingDelimiters(CFDictionaryGetValue(certRecord, kCertStatsIssuerSummary), kCertStatsDelimiters);
522
523 /* this generates a key which includes delimited fields to identify the certificate */
524 CFStringRef keyStr = CFStringCreateWithFormat(NULL, NULL,
525 kCertStatsFormat, /* format string */
526 kCertStatsCert, /* certificate key */
527 kCertStatsPolicy, policy,
528 kCertStatsNotBefore, notBefore,
529 kCertStatsNotAfter, notAfter,
530 kCertStatsSerialNumber, serial,
531 kCertStatsSubjectSummary, subject,
532 kCertStatsIssuerSummary, issuer
533 );
534 CFReleaseSafe(policy);
535 CFReleaseSafe(notBefore);
536 CFReleaseSafe(notAfter);
537 CFReleaseSafe(serial);
538 CFReleaseSafe(subject);
539 CFReleaseSafe(issuer);
540
541 result = SetCertificateTraceValueForKey(keyStr, 1);
542 #if DEBUG
543 secerror("%@.%@ (%d)", kCertStatsPrefix, keyStr, (result) ? 1 : 0);
544 #endif
545 CFReleaseSafe(keyStr);
546
547 return result;
548 }
549