]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSLogger.m
Security-58286.20.16.tar.gz
[apple/security.git] / keychain / ckks / CKKSLogger.m
1 /*
2 * Copyright (c) 2017 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 #if OCTAGON
25
26 #import "CKKSLogger.h"
27 #import "debugging.h"
28 #import "CKKS.h"
29 #import "CKKSViewManager.h"
30 #import "keychain/ckks/CKKSKeychainView.h"
31 #include <utilities/SecFileLocations.h>
32 #import <Security/SFSQLite.h>
33 #import <os/log.h>
34
35 NSString* const CKKSLoggerTableSuccessCount = @"success_count";
36 NSString* const CKKSLoggerColumnEventType = @"event_type";
37 NSString* const CKKSLoggerColumnSuccessCount = @"success_count";
38 NSString* const CKKSLoggerColumnFailureCount = @"failure_count";
39
40 NSString* const CKKSLoggerTableFailures = @"failures";
41 NSString* const CKKSLoggerColumnData = @"data";
42
43 NSString* const CKKSLoggerUploadDate = @"upload_date";
44 NSString* const CKKSLoggerLastClassASync = @"last_class_a_sync";
45 NSString* const CKKSLoggerLastClassCSync = @"last_class_c_sync";
46
47 NSString* const CKKSLoggerDaysSinceLastSyncClassA = @"lastSyncClassA";
48 NSString* const CKKSLoggerDaysSinceLastSyncClassC = @"lastSyncClassC";
49
50 NSString* const CKKSLoggerSplunkTopic = @"topic";
51 NSString* const CKKSLoggerSplunkEventTime = @"eventTime";
52 NSString* const CKKSLoggerSplunkPostTime = @"postTime";
53 NSString* const CKKSLoggerSplunkEvents = @"events";
54 NSString* const CKKSLoggerSplunkEventType = @"eventType";
55 NSString* const CKKSLoggerMetricsBase = @"metricsBase";
56
57 NSString* const CKKSLoggerValueSuccess = @"success";
58
59 #define CKKS_SPLUNK_DEV 0
60
61 #if CKKS_SPLUNK_DEV
62 #define SECONDS_BETWEEN_UPLOADS 10
63 #else
64 // three days = 60 seconds times 60 minutes * 72 hours
65 #define SECONDS_BETWEEN_UPLOADS (60 * 60 * 72)
66 #endif
67
68 NSString* const CKKSLoggingTableSchema = @"CREATE TABLE IF NOT EXISTS failures (\n"
69 @"id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
70 @"data BLOB\n"
71 @");\n"
72 @"CREATE TRIGGER IF NOT EXISTS maintain_ring_buffer AFTER INSERT ON failures\n"
73 @"BEGIN\n"
74 @"DELETE FROM failures WHERE id != NEW.id AND id % 999 = NEW.id % 999;\n"
75 @"END;\n"
76 @"CREATE TABLE IF NOT EXISTS success_count (\n"
77 @"event_type STRING PRIMARY KEY,\n"
78 @"success_count INTEGER,\n"
79 @"failure_count INTEGER\n"
80 @");\n";
81
82 static NSString* CKKSLoggingTablePath()
83 {
84 return [(__bridge_transfer NSURL*)SecCopyURLForFileInKeychainDirectory((__bridge CFStringRef)@"ckks_analytics_v1.db") path];
85 }
86
87 @interface CKKSLoggerSQLiteStore : SFSQLite
88
89 + (instancetype)sharedStore;
90
91 @property (readonly, strong) NSArray* failureRecords;
92 @property (readwrite, strong) NSDate* uploadDate;
93
94 - (void)incrementSuccessCountForEventType:(NSString*)eventType;
95 - (void)incrementFailureCountForEventType:(NSString*)eventType;
96 - (NSInteger)successCountForEventType:(NSString*)eventType;
97 - (NSInteger)failureCountForEventType:(NSString*)eventType;
98 - (void)addFailureRecord:(NSDictionary*)valueDict;
99 - (void)clearAllData;
100
101 - (NSDictionary*)summaryCounts;
102
103 @end
104
105 @implementation CKKSLogger {
106 NSURL* _splunkUploadURL;
107 NSString* _splunkTopicName;
108 NSURL* _splunkBagURL;
109 dispatch_queue_t _queue;
110 NSInteger _secondsBetweenUploads;
111 NSDictionary* _metricsBase; // data the server provides and wants us to send back
112 NSArray* _blacklistedFields;
113 NSArray* _blacklistedEvents;
114
115 unsigned int _allowInsecureSplunkCert:1;
116 unsigned int _disableLogging:1;
117 unsigned int _disableUploads:1;
118 unsigned int _ignoreServersMessagesTellingUsToGoAway:1;
119 }
120
121 @synthesize splunkUploadURL = _splunkUploadURL;
122 @synthesize splunkBagURL = _splunkBagURL;
123 @synthesize splunkTopicName = _splunkTopicName;
124 @synthesize splunkLoggingQueue = _queue;
125
126 + (instancetype)logger
127 {
128 #if TARGET_OS_SIMULATOR
129 return nil;
130 #endif
131 static CKKSLogger* __sharedLogger;
132 static dispatch_once_t onceToken;
133 dispatch_once(&onceToken, ^{
134 __sharedLogger = [[CKKSLogger alloc] init];
135 });
136
137 return __sharedLogger;
138 }
139
140 - (instancetype)init
141 {
142 if (self = [super init]) {
143 _queue = dispatch_queue_create("com.apple.security.ckks.logging", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
144 _secondsBetweenUploads = SECONDS_BETWEEN_UPLOADS;
145
146 NSDictionary* systemDefaultValues = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle bundleWithPath:@"/System/Library/Frameworks/Security.framework"] pathForResource:@"CKKSLogging" ofType:@"plist"]];
147 _splunkTopicName = systemDefaultValues[@"splunk_topic"];
148 _splunkUploadURL = [NSURL URLWithString:systemDefaultValues[@"splunk_uploadURL"]];
149 _splunkBagURL = [NSURL URLWithString:systemDefaultValues[@"splunk_bagURL"]];
150 _allowInsecureSplunkCert = [[systemDefaultValues valueForKey:@"splunk_allowInsecureCertificate"] boolValue];
151 NSString* splunkEndpoint = systemDefaultValues[@"splunk_endpointDomain"];
152
153 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
154 NSString* userDefaultsSplunkTopic = [defaults stringForKey:@"splunk_topic"];
155 if (userDefaultsSplunkTopic) {
156 _splunkTopicName = userDefaultsSplunkTopic;
157 }
158
159 NSURL* userDefaultsSplunkUploadURL = [NSURL URLWithString:[defaults stringForKey:@"splunk_uploadURL"]];
160 if (userDefaultsSplunkUploadURL) {
161 _splunkUploadURL = userDefaultsSplunkUploadURL;
162 }
163
164 NSURL* userDefaultsSplunkBagURL = [NSURL URLWithString:[defaults stringForKey:@"splunk_bagURL"]];
165 if (userDefaultsSplunkUploadURL) {
166 _splunkBagURL = userDefaultsSplunkBagURL;
167 }
168
169 BOOL userDefaultsAllowInsecureSplunkCert = [defaults boolForKey:@"splunk_allowInsecureCertificate"];
170 _allowInsecureSplunkCert |= userDefaultsAllowInsecureSplunkCert;
171
172 NSString* userDefaultsSplunkEndpoint = [defaults stringForKey:@"splunk_endpointDomain"];
173 if (userDefaultsSplunkEndpoint) {
174 splunkEndpoint = userDefaultsSplunkEndpoint;
175 }
176
177 #if CKKS_SPLUNK_DEV
178 _ignoreServersMessagesTellingUsToGoAway = YES;
179
180 if (!_splunkUploadURL && splunkEndpoint) {
181 NSString* urlString = [NSString stringWithFormat:@"https://%@/report/2/%@", splunkEndpoint, _splunkTopicName];
182 _splunkUploadURL = [NSURL URLWithString:urlString];
183 }
184 #else
185 (void)splunkEndpoint;
186 #endif
187 }
188
189 return self;
190 }
191
192 - (void)setLastSuccessfulClassASyncDate:(NSDate*)lastSuccessfulClassASyncDate
193 {
194 dispatch_sync(_queue, ^{
195 [[CKKSLoggerSQLiteStore sharedStore] setDateProperty:lastSuccessfulClassASyncDate forKey:CKKSLoggerLastClassASync];
196 });
197 }
198
199 - (NSDate*)lastSuccessfulClassASyncDate
200 {
201 __block NSDate* result = nil;
202 dispatch_sync(_queue, ^{
203 result = [self _onQueueLastSuccessfulClassASyncDate];
204 });
205
206 return result;
207 }
208
209 - (NSDate*)_onQueueLastSuccessfulClassASyncDate
210 {
211 dispatch_assert_queue(_queue);
212 return [[CKKSLoggerSQLiteStore sharedStore] datePropertyForKey:CKKSLoggerLastClassASync] ?: [NSDate distantPast];
213 }
214
215 - (void)setLastSuccessfulClassCSyncDate:(NSDate*)lastSuccessfulClassCSyncDate
216 {
217 dispatch_sync(_queue, ^{
218 [[CKKSLoggerSQLiteStore sharedStore] setDateProperty:lastSuccessfulClassCSyncDate forKey:CKKSLoggerLastClassCSync];
219 });
220 }
221
222 - (NSDate*)lastSuccessfulClassCSyncDate
223 {
224 __block NSDate* result = nil;
225 dispatch_sync(_queue, ^{
226 result = [self _onQueueLastSuccessfulClassCSyncDate];
227 });
228
229 return result;
230 }
231
232 - (NSDate*)_onQueueLastSuccessfulClassCSyncDate
233 {
234 dispatch_assert_queue(_queue);
235 return [[CKKSLoggerSQLiteStore sharedStore] datePropertyForKey:CKKSLoggerLastClassCSync] ?: [NSDate distantPast];
236 }
237
238 - (void)logSuccessForEventNamed:(NSString*)eventName
239 {
240 [self logEventNamed:eventName value:nil isSuccess:YES];
241 }
242
243 - (void)logFailureForEventNamed:(NSString*)eventName withAttributes:(NSDictionary*)attributes
244 {
245 [self logEventNamed:eventName value:attributes isSuccess:NO];
246 }
247
248 - (void)logEventNamed:(NSString*)eventName value:(NSDictionary*)valueDict isSuccess:(BOOL)isSuccess
249 {
250 __weak __typeof(self) weakSelf = self;
251 dispatch_async(_queue, ^{
252
253 __strong __typeof(self) strongSelf = weakSelf;
254 if (!strongSelf) {
255 return;
256 }
257
258 if (strongSelf->_disableLogging || [strongSelf->_blacklistedEvents containsObject:eventName]) {
259 return;
260 }
261
262 CKKSLoggerSQLiteStore* store = [CKKSLoggerSQLiteStore sharedStore];
263 if (isSuccess) {
264 [store incrementSuccessCountForEventType:eventName];
265 }
266 else {
267 [store incrementFailureCountForEventType:eventName];
268 NSMutableDictionary* eventDict = valueDict.mutableCopy;
269 eventDict[CKKSLoggerSplunkTopic] = strongSelf->_splunkTopicName;
270 eventDict[CKKSLoggerSplunkEventType] = eventName;
271 eventDict[CKKSLoggerSplunkEventTime] = @([[NSDate date] timeIntervalSince1970] * 1000);
272 eventDict[CKKSLoggerMetricsBase] = strongSelf->_metricsBase ?: [NSDictionary dictionary];
273
274 for (NSString* blacklistedField in strongSelf->_blacklistedFields) {
275 [eventDict removeObjectForKey:blacklistedField];
276 }
277
278 [store addFailureRecord:eventDict];
279 }
280
281 NSDate* uploadDate = store.uploadDate;
282 NSDate* nowDate = [NSDate date];
283 if (uploadDate) {
284 if ([nowDate compare:uploadDate] == NSOrderedDescending) {
285 [self _onQueueUploadDataWithError:nil];
286 }
287 }
288 else {
289 store.uploadDate = [nowDate dateByAddingTimeInterval:strongSelf->_secondsBetweenUploads];
290 }
291 });
292 }
293
294 // this method is kind of evil for the fact that it has side-effects in pulling other things besides the metricsURL from the server, and as such should NOT be memoized.
295 // TODO redo this, probably to return a dictionary.
296 - (NSURL*)splunkUploadURL
297 {
298 dispatch_assert_queue(_queue);
299
300 if (_splunkUploadURL) {
301 return _splunkUploadURL;
302 }
303
304 __weak __typeof(self) weakSelf = self;
305 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
306
307 __block NSError* error = nil;
308 NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
309 NSURLSession* storeBagSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
310 delegate:self
311 delegateQueue:nil];
312
313 NSURL* requestEndpoint = _splunkBagURL;
314 __block NSURL* result = nil;
315 NSURLSessionDataTask* storeBagTask = [storeBagSession dataTaskWithURL:requestEndpoint completionHandler:^(NSData * _Nullable data,
316 NSURLResponse * _Nullable __unused response,
317 NSError * _Nullable responseError) {
318
319 __strong __typeof(self) strongSelf = weakSelf;
320 if (!strongSelf) {
321 return;
322 }
323
324 if (data && !responseError) {
325 NSData *responseData = data; // shut up compiler
326 NSDictionary* responseDict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
327 if([responseDict isKindOfClass:NSDictionary.class] && !error) {
328 if (!self->_ignoreServersMessagesTellingUsToGoAway) {
329 strongSelf->_disableLogging = [[responseDict valueForKey:@"disabled"] boolValue];
330 if (strongSelf->_disableLogging || [[responseDict valueForKey:@"sendDisabled"] boolValue]) {
331 // then don't upload anything right now
332 secerror("not returning a splunk URL because uploads are disabled");
333 dispatch_semaphore_signal(sem);
334 return;
335 }
336
337 NSUInteger millisecondsBetweenUploads = [[responseDict valueForKey:@"postFrequency"] unsignedIntegerValue] / 1000;
338 if (millisecondsBetweenUploads > 0) {
339 strongSelf->_secondsBetweenUploads = millisecondsBetweenUploads;
340 }
341
342 strongSelf->_blacklistedEvents = responseDict[@"blacklistedEvents"];
343 strongSelf->_blacklistedFields = responseDict[@"blacklistedFields"];
344 }
345
346 strongSelf->_metricsBase = responseDict[@"metricsBase"];
347
348 NSString* metricsEndpoint = responseDict[@"metricsUrl"];
349 if([metricsEndpoint isKindOfClass:NSString.class]) {
350 /* Lives our URL */
351 NSString* endpoint = [metricsEndpoint stringByAppendingFormat:@"/2/%@", strongSelf->_splunkTopicName];
352 secnotice("ckks", "got metrics endpoint: %@", endpoint);
353 NSURL* endpointURL = [NSURL URLWithString:endpoint];
354 if([endpointURL.scheme isEqualToString:@"https"]) {
355 result = endpointURL;
356 }
357 }
358 }
359 }
360 else {
361 error = responseError;
362 }
363 if(error) {
364 secnotice("ckks", "Unable to fetch splunk endpoint at URL: %@ -- error: %@", requestEndpoint, error.description);
365 }
366 else if(!result) {
367 secnotice("ckks", "Malformed iTunes config payload!");
368 }
369
370 dispatch_semaphore_signal(sem);
371 }];
372
373 [storeBagTask resume];
374 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
375
376 return result;
377 }
378
379 - (BOOL)forceUploadWithError:(NSError**)error
380 {
381 __block BOOL result = NO;
382 dispatch_sync(_queue, ^{
383 result = [self _onQueueUploadDataWithError:error];
384 });
385 return result;
386 }
387
388 - (BOOL)_onQueueUploadDataWithError:(NSError**)error
389 {
390 dispatch_assert_queue(_queue);
391
392 NSData* json = [self _onQueueGetLoggingJSONWithError:error];
393 if (json && [self _onQueuePostJSON:json error:error]) {
394 secinfo("ckks", "uploading sync health data: %@", json);
395
396 CKKSLoggerSQLiteStore* store = [CKKSLoggerSQLiteStore sharedStore];
397 [store clearAllData];
398 store.uploadDate = [NSDate dateWithTimeIntervalSinceNow:_secondsBetweenUploads];
399 return YES;
400 }
401 else {
402 return NO;
403 }
404 }
405
406 - (BOOL)_onQueuePostJSON:(NSData*)json error:(NSError**)error
407 {
408 dispatch_assert_queue(_queue);
409
410 /*
411 * Create the NSURLSession
412 * We use the ephemeral session config because we don't need cookies or cache
413 */
414 NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
415 NSURLSession* postSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
416 delegate:self
417 delegateQueue:nil];
418
419 /*
420 * Create the request
421 */
422 NSURL* postEndpoint = self.splunkUploadURL;
423 if (!postEndpoint) {
424 secerror("failed to get a splunk upload endpoint - not uploading");
425 return NO;
426 }
427
428 NSMutableURLRequest* postRequest = [[NSMutableURLRequest alloc] init];
429 postRequest.URL = postEndpoint;
430 postRequest.HTTPMethod = @"POST";
431 postRequest.HTTPBody = json;
432
433 /*
434 * Create the upload task.
435 */
436 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
437 __block BOOL uploadSuccess = NO;
438 NSURLSessionDataTask* uploadTask = [postSession dataTaskWithRequest:postRequest
439 completionHandler:^(NSData * _Nullable __unused data, NSURLResponse * _Nullable response, NSError * _Nullable requestError) {
440 if(requestError) {
441 secerror("Error in uploading the events to splunk: %@", requestError);
442 }
443 else if (![response isKindOfClass:NSHTTPURLResponse.class]){
444 Class class = response.class;
445 secerror("Received the wrong kind of response: %@", NSStringFromClass(class));
446 }
447 else {
448 NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
449 if(httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
450 /* Success */
451 uploadSuccess = YES;
452 secnotice("ckks", "Splunk upload success");
453 }
454 else {
455 secnotice("ckks", "Splunk upload unexpected status to URL: %@ -- status: %d", postEndpoint, (int)(httpResponse.statusCode));
456 }
457 }
458 dispatch_semaphore_signal(sem);
459 }];
460
461 secnotice("ckks", "Splunk upload start");
462 [uploadTask resume];
463 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
464 return uploadSuccess;
465 }
466
467 #define SECOND_PER_DAY (60 * 60 * 24)
468
469 - (NSInteger)fuzzyDaysSinceDate:(NSDate*)date
470 {
471 NSTimeInterval timeIntervalSinceDate = [[NSDate date] timeIntervalSinceDate:date];
472 if (timeIntervalSinceDate < SECOND_PER_DAY) {
473 return 0;
474 }
475 else if (timeIntervalSinceDate < (SECOND_PER_DAY * 7)) {
476 return 1;
477 }
478 else if (timeIntervalSinceDate < (SECOND_PER_DAY * 30)) {
479 return 7;
480 }
481 else if (timeIntervalSinceDate < (SECOND_PER_DAY * 365)) {
482 return 30;
483 }
484 else {
485 return 365;
486 }
487 }
488
489 - (NSData*)getLoggingJSONWithError:(NSError**)error
490 {
491 __block NSData* json = nil;
492 dispatch_sync(_queue, ^{
493 json = [self _onQueueGetLoggingJSONWithError:error];
494 });
495
496 return json;
497 }
498
499 - (NSData*)_onQueueGetLoggingJSONWithError:(NSError**)error
500 {
501 dispatch_assert_queue(_queue);
502
503 CKKSLoggerSQLiteStore* store = [CKKSLoggerSQLiteStore sharedStore];
504 NSArray* failureRecords = [store failureRecords];
505
506 NSDictionary* successCounts = [store summaryCounts];
507 NSInteger totalSuccessCount = 0;
508 NSInteger totalFailureCount = 0;
509 for (NSDictionary* perEventTypeSuccessCounts in successCounts.objectEnumerator) {
510 totalSuccessCount += [perEventTypeSuccessCounts[CKKSLoggerColumnSuccessCount] integerValue];
511 totalFailureCount += [perEventTypeSuccessCounts[CKKSLoggerColumnFailureCount] integerValue];
512 }
513
514 NSDate* now = [NSDate date];
515
516 NSMutableDictionary* healthSummaryEvent = [[NSMutableDictionary alloc] init];
517 healthSummaryEvent[CKKSLoggerSplunkTopic] = _splunkTopicName ?: [NSNull null];
518 healthSummaryEvent[CKKSLoggerSplunkEventTime] = @([now timeIntervalSince1970] * 1000);
519 healthSummaryEvent[CKKSLoggerSplunkEventType] = @"manifestHealthSummary";
520 healthSummaryEvent[CKKSLoggerColumnSuccessCount] = @(totalSuccessCount);
521 healthSummaryEvent[CKKSLoggerColumnFailureCount] = @(totalFailureCount);
522 healthSummaryEvent[CKKSLoggerMetricsBase] = _metricsBase ?: [NSDictionary dictionary];
523
524 for (NSString* viewName in [CKKSViewManager viewList]) {
525 CKKSKeychainView* view = [CKKSViewManager findOrCreateView:viewName];
526 [healthSummaryEvent setValue:@([self fuzzyDaysSinceDate:[self _onQueueLastSuccessfulClassASyncDate]]) forKey:[NSString stringWithFormat:@"%@-%@", view.zoneName, CKKSLoggerDaysSinceLastSyncClassA]];
527 [healthSummaryEvent setValue:@([self fuzzyDaysSinceDate:[self _onQueueLastSuccessfulClassCSyncDate]]) forKey:[NSString stringWithFormat:@"%@-%@", view.zoneName, CKKSLoggerDaysSinceLastSyncClassC]];
528 }
529
530 NSMutableArray* splunkRecords = failureRecords.mutableCopy;
531 [splunkRecords addObject:healthSummaryEvent];
532
533 NSDictionary* jsonDict = @{CKKSLoggerSplunkPostTime : @([now timeIntervalSince1970] * 1000), @"events" : splunkRecords};
534
535 return [NSJSONSerialization dataWithJSONObject:jsonDict options:NSJSONWritingPrettyPrinted error:error];
536 }
537
538 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
539 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
540 assert(completionHandler);
541 (void)session;
542 secnotice("ckks", "Splunk upload challenge");
543 NSURLCredential *cred = nil;
544 SecTrustResultType result = kSecTrustResultInvalid;
545
546 if ([challenge previousFailureCount] > 0) {
547 // Previous failures occurred, bail
548 completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
549
550 } else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
551 /*
552 * Evaluate trust for the certificate
553 */
554
555 SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
556
557 OSStatus status = SecTrustEvaluate(serverTrust, &result);
558 if (status == errSecSuccess && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)) {
559 /*
560 * All is well, accept the credentials
561 */
562
563 cred = [NSURLCredential credentialForTrust:serverTrust];
564 completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
565 } else if (_allowInsecureSplunkCert) {
566 secnotice("ckks", "Force Accepting Splunk Credential");
567
568 cred = [NSURLCredential credentialForTrust:serverTrust];
569 completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
570 } else {
571 /*
572 * An error occurred in evaluating trust, bail
573 */
574 completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
575 }
576 } else {
577 /*
578 * Just perform the default handling
579 */
580 completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
581 }
582
583 }
584
585 - (BOOL)ignoreServerDisablingMessages
586 {
587 return _ignoreServersMessagesTellingUsToGoAway;
588 }
589
590 - (void)setIgnoreServerDisablingMessages:(BOOL)ignoreServer
591 {
592 _ignoreServersMessagesTellingUsToGoAway = ignoreServer ? YES : NO;
593 }
594
595 @end
596
597 @implementation CKKSLoggerSQLiteStore
598
599 + (instancetype)sharedStore
600 {
601 static CKKSLoggerSQLiteStore* store = nil;
602 static dispatch_once_t onceToken;
603 dispatch_once(&onceToken, ^{
604 store = [[self alloc] initWithPath:CKKSLoggingTablePath() schema:CKKSLoggingTableSchema];
605 [store open];
606 });
607
608 return store;
609 }
610
611 - (void)dealloc
612 {
613 [self close];
614 }
615
616 - (NSInteger)successCountForEventType:(NSString*)eventType
617 {
618 return [[[[self select:@[CKKSLoggerColumnSuccessCount] from:CKKSLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:CKKSLoggerColumnSuccessCount] integerValue];
619 }
620
621 - (void)incrementSuccessCountForEventType:(NSString*)eventType
622 {
623 @try {
624 NSInteger successCount = [self successCountForEventType:eventType];
625 NSInteger failureCount = [self failureCountForEventType:eventType];
626 [self insertOrReplaceInto:CKKSLoggerTableSuccessCount values:@{CKKSLoggerColumnEventType : eventType, CKKSLoggerColumnSuccessCount : @(successCount + 1), CKKSLoggerColumnFailureCount : @(failureCount)}];
627 } @catch (id ue) {
628 secerror("incrementSuccessCountForEventType exception: %@", ue);
629 }
630 }
631
632 - (NSInteger)failureCountForEventType:(NSString*)eventType
633 {
634 return [[[[self select:@[CKKSLoggerColumnFailureCount] from:CKKSLoggerTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:CKKSLoggerColumnFailureCount] integerValue];
635 }
636
637 - (void)incrementFailureCountForEventType:(NSString*)eventType
638 {
639 @try {
640 NSInteger successCount = [self successCountForEventType:eventType];
641 NSInteger failureCount = [self failureCountForEventType:eventType];
642 [self insertOrReplaceInto:CKKSLoggerTableSuccessCount values:@{CKKSLoggerColumnEventType : eventType, CKKSLoggerColumnSuccessCount : @(successCount), CKKSLoggerColumnFailureCount : @(failureCount + 1)}];
643 } @catch (id ue) {
644 secerror("incrementFailureCountForEventType exception: %@", ue);
645 }
646 }
647
648 - (NSDictionary*)summaryCounts
649 {
650 NSMutableDictionary* successCountsDict = [NSMutableDictionary dictionary];
651 NSArray* rows = [self selectAllFrom:CKKSLoggerTableSuccessCount where:nil bindings:nil];
652 for (NSDictionary* rowDict in rows) {
653 successCountsDict[rowDict[CKKSLoggerColumnEventType]] = @{CKKSLoggerColumnSuccessCount : rowDict[CKKSLoggerColumnSuccessCount], CKKSLoggerColumnFailureCount : rowDict[CKKSLoggerColumnFailureCount]};
654 }
655
656 return successCountsDict;
657 }
658
659 - (NSArray*)failureRecords
660 {
661 NSArray* recordBlobs = [self select:@[CKKSLoggerColumnData] from:CKKSLoggerTableFailures];
662
663 NSMutableArray* failureRecords = [[NSMutableArray alloc] init];
664 for (NSDictionary* row in recordBlobs) {
665 NSDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[CKKSLoggerColumnData] options:0 format:nil error:nil];
666 [failureRecords addObject:deserializedRecord];
667 }
668
669 return failureRecords;
670 }
671
672 - (void)addFailureRecord:(NSDictionary*)valueDict
673 {
674 @try {
675 NSError* error = nil;
676 NSData* serializedRecord = [NSPropertyListSerialization dataWithPropertyList:valueDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
677 if(!error && serializedRecord) {
678 [self insertOrReplaceInto:CKKSLoggerTableFailures values:@{CKKSLoggerColumnData : serializedRecord}];
679 }
680 if(error && !serializedRecord) {
681 secerror("Couldn't serialize failure record: %@", error);
682 }
683 } @catch (id ue) {
684 secerror("addFailureRecord exception: %@", ue);
685 }
686 }
687
688 - (NSDate*)uploadDate
689 {
690 return [self datePropertyForKey:CKKSLoggerUploadDate];
691 }
692
693 - (void)setUploadDate:(NSDate*)uploadDate
694 {
695 [self setDateProperty:uploadDate forKey:CKKSLoggerUploadDate];
696 }
697
698 - (void)clearAllData
699 {
700 [self deleteFrom:CKKSLoggerTableSuccessCount where:@"event_type like ?" bindings:@[@"%"]];
701 [self deleteFrom:CKKSLoggerTableFailures where:@"id >= 0" bindings:nil];
702 }
703
704 @end
705
706 #endif // OCTAGON