]> git.saurik.com Git - apple/security.git/blob - OSX/utilities/SecTapToRadar.m
Security-59306.101.1.tar.gz
[apple/security.git] / OSX / utilities / SecTapToRadar.m
1 /*
2 * Copyright (c) 2019 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 #import "SecTapToRadar.h"
25 #import "utilities/debugging.h"
26
27 #if TARGET_OS_IOS
28
29 #import <MobileCoreServices/LSApplicationWorkspace.h>
30 #import <CoreFoundation/CFUserNotification.h>
31 #import <SoftLinking/SoftLinking.h>
32
33 #pragma clang diagnostic push
34 #pragma clang diagnostic ignored "-Wunused-function"
35 SOFT_LINK_FRAMEWORK(Frameworks, MobileCoreServices)
36 SOFT_LINK_CLASS(MobileCoreServices, LSApplicationWorkspace);
37 #pragma clang diagnostic pop
38
39 #endif
40
41 #include "utilities/SecInternalReleasePriv.h"
42
43 static NSString* kSecNextTTRDate = @"NextTTRDate";
44 static NSString* kSecPreferenceDomain = @"com.apple.security";
45
46 @interface SecTapToRadar ()
47 @property (readwrite) NSString *alert;
48 @property (readwrite) NSString *radarDescription;
49 @property (readwrite) NSString *radarnumber;
50 @property (readwrite) dispatch_queue_t queue;
51
52 @end
53
54 static BOOL SecTTRDisabled = true;
55
56 @implementation SecTapToRadar
57
58 - (instancetype)initTapToRadar:(NSString *)alert
59 description:(NSString *)radarDescription
60 radar:(NSString *)radarnumber
61 {
62 if ((self = [super init]) == nil) {
63 return nil;
64 }
65
66 _alert = alert;
67 _radarDescription = radarDescription;
68 _radarnumber = radarnumber;
69 _queue = dispatch_queue_create("com.apple.security.diagnostic-queue", 0);
70 dispatch_set_target_queue(_queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
71
72 _componentName = @"Security";
73 _componentVersion = @"all";
74 _componentID = @"606179";
75
76 return self;
77 }
78
79 + (NSString *)keyname:(SecTapToRadar *)ttrRequest
80 {
81 return [NSString stringWithFormat:@"%@-%@", kSecNextTTRDate, ttrRequest.radarnumber];
82 }
83
84 + (BOOL)isRateLimited:(SecTapToRadar *)ttrRequest
85 {
86 return SecTTRDisabled || [ttrRequest isRateLimited];
87 }
88
89 - (BOOL)isRateLimited
90 {
91 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:kSecPreferenceDomain];
92
93 NSString *key = [[self class] keyname: self];
94 NSDate *val = [defaults valueForKey:key];
95 if (![val isKindOfClass:[NSDate class]]) {
96 [defaults removeObjectForKey:key];
97 return NO;
98 }
99
100 if ([val compare:[NSDate date]] == NSOrderedAscending) {
101 return NO;
102 }
103
104 return YES;
105 }
106
107 + (void)disableTTRsEntirely {
108 SecTTRDisabled = YES;
109 }
110
111
112 - (void)updateRetryTimestamp
113 {
114 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:kSecPreferenceDomain];
115 [defaults setObject:[[NSDate date] dateByAddingTimeInterval:24*3600.0]
116 forKey:[[self class] keyname: self]];
117 }
118
119 - (void)clearRetryTimestamp
120 {
121 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:kSecPreferenceDomain];
122 [defaults removeObjectForKey:[[self class] keyname: self]];
123 }
124
125
126 + (void)triggerTapToRadar:(SecTapToRadar *)ttrRequest
127 {
128 secnotice("secttr", "Triggering TTR: %@", ttrRequest.alert);
129 dispatch_assert_queue(ttrRequest.queue);
130
131 #if TARGET_OS_IOS
132 static dispatch_once_t onceToken;
133 static NSMutableCharacterSet *queryComponent;
134 dispatch_once(&onceToken, ^{
135 queryComponent = [[NSMutableCharacterSet alloc] init];
136 [queryComponent formUnionWithCharacterSet:[NSCharacterSet URLQueryAllowedCharacterSet]];
137 [queryComponent removeCharactersInString:@"&"];
138 });
139
140 Class workspaceCls = getLSApplicationWorkspaceClass();
141 if (workspaceCls == NULL) {
142 return;
143 }
144
145 NSString *title = [NSString stringWithFormat:@"Triggered SecTTR: %@ - %@", ttrRequest.alert, ttrRequest.radarnumber];
146 NSString *encodedTitle = [title stringByAddingPercentEncodingWithAllowedCharacters:queryComponent];
147
148 NSString *desc = [NSString stringWithFormat:@"%@\nRelated radar: radr://%@", ttrRequest.radarDescription, ttrRequest.radarnumber];
149 NSString *encodedDesc = [desc stringByAddingPercentEncodingWithAllowedCharacters:queryComponent];
150
151 NSString *url = [NSString stringWithFormat:@"tap-to-radar://new?"
152 "Reproducibilty=Always&"
153 "Title=%@&"
154 "ComponentName=%@&"
155 "ComponentVersion=%@&"
156 "Reproducibility=Not%%20Applicable&"
157 "ComponentID=%@&"
158 "Classification=Crash/Hang/Data%%20Loss&"
159 "Description=%@",
160 encodedTitle,
161 [ttrRequest.componentName stringByAddingPercentEncodingWithAllowedCharacters:queryComponent],
162 [ttrRequest.componentVersion stringByAddingPercentEncodingWithAllowedCharacters:queryComponent],
163 [ttrRequest.componentID stringByAddingPercentEncodingWithAllowedCharacters:queryComponent],
164 encodedDesc];
165
166 NSURL *tapToRadarURL = [NSURL URLWithString:url];
167 [[workspaceCls defaultWorkspace] openURL:tapToRadarURL configuration:nil completionHandler:^(NSDictionary<NSString *,id> * __unused result, NSError * __unused error) {
168 }];
169 #endif
170 }
171
172 + (BOOL)askUserIfTTR:(SecTapToRadar *)ttrRequest
173 {
174 BOOL result = NO;
175
176 #if TARGET_OS_IOS
177 NSDictionary *alertOptions = @{
178 (NSString *)kCFUserNotificationDefaultButtonTitleKey : @"Tap-To-Radar",
179 (NSString *)kCFUserNotificationAlternateButtonTitleKey : @"Go away",
180 (NSString *)kCFUserNotificationAlertMessageKey : ttrRequest.alert,
181 (NSString *)kCFUserNotificationAlertHeaderKey : @"Security",
182 };
183
184 SInt32 error = 0;
185 CFUserNotificationRef notification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &error, (__bridge CFDictionaryRef)alertOptions);
186 if (notification != NULL) {
187 CFOptionFlags responseFlags = 0;
188 CFUserNotificationReceiveResponse(notification, 1.0 * 60 * 3, &responseFlags);
189 switch (responseFlags & 0x03) {
190 case kCFUserNotificationDefaultResponse:
191 result = YES;
192 break;
193 default:
194 break;
195 }
196 CFRelease(notification);
197 } else {
198 secnotice("SecTTR", "Failed to create notification error %@", @(error));
199 }
200 #endif
201 return result;
202 }
203
204 - (void)trigger
205 {
206 dispatch_sync(self.queue, ^{
207 @autoreleasepool {
208 if (!SecIsInternalRelease()) {
209 return;
210 }
211
212 if ([[self class] isRateLimited:self]) {
213 secnotice("SecTTR", "Not showing ttr due to ratelimiting: %@", self.alert);
214 return;
215 }
216
217 if ([[self class] askUserIfTTR:self]) {
218 [[self class] triggerTapToRadar:self];
219 }
220 [self updateRetryTimestamp];
221 }
222 });
223 }
224
225 @end