]> git.saurik.com Git - apple/security.git/blob - keychain/otpaird/main.m
Security-59306.101.1.tar.gz
[apple/security.git] / keychain / otpaird / main.m
1 #import <TargetConditionals.h>
2 #import <Foundation/Foundation.h>
3 #import <Security/SecXPCHelper.h>
4 #import <xpc/xpc.h>
5 #if TARGET_OS_WATCH
6 #import <xpc/private.h>
7 #endif /* TARGET_OS_WATCH */
8
9 #import "OTPairingService.h"
10 #import "OTPairingConstants.h"
11
12 #if TARGET_OS_WATCH
13 static void create_xpc_listener(xpc_handler_t handler);
14 static void handle_pairing_result(bool success, NSError *error);
15 static void pairing_retry_register(bool checkin);
16 static void pairing_retry_unregister(void);
17 static void ids_retry_init(void);
18 static void ids_retry_enable(bool);
19 #endif /* TARGET_OS_WATCH */
20
21 int
22 main()
23 {
24 static OTPairingService *service;
25
26 @autoreleasepool {
27 service = [OTPairingService sharedService];
28 }
29
30 #if TARGET_OS_WATCH
31 /* Check in; handle a possibly-pending retry. */
32 pairing_retry_register(true);
33
34 ids_retry_init();
35
36 create_xpc_listener(^(xpc_object_t message) {
37 xpc_object_t reply;
38
39 reply = xpc_dictionary_create_reply(message);
40
41 /* Received an explicit pairing request; remove retry activity if one exists. */
42 pairing_retry_unregister();
43
44 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
45 xpc_connection_t connection;
46
47 if (success) {
48 os_log(OS_LOG_DEFAULT, "xpc-initiated pairing succeeded");
49 } else {
50 os_log(OS_LOG_DEFAULT, "xpc-initiated pairing failed: %@", error);
51 }
52
53 xpc_dictionary_set_bool(reply, OTPairingXPCKeySuccess, success);
54 if (error) {
55 NSData *errdata = [SecXPCHelper encodedDataFromError:error];
56 xpc_dictionary_set_data(reply, OTPairingXPCKeyError, errdata.bytes, errdata.length);
57 }
58 connection = xpc_dictionary_get_remote_connection(reply);
59 xpc_connection_send_message(connection, reply);
60
61 handle_pairing_result(success, error);
62 }];
63 });
64 #endif /* TARGET_OS_WATCH */
65
66 dispatch_main();
67 }
68
69 #if TARGET_OS_WATCH
70 static void
71 create_xpc_listener(xpc_handler_t handler)
72 {
73 static xpc_connection_t listener;
74
75 listener = xpc_connection_create_mach_service(OTPairingMachServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
76 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
77 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION) {
78 return;
79 }
80
81 // TODO: entitlement check
82
83 xpc_connection_set_event_handler(peer, ^(xpc_object_t message) {
84 if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) {
85 return;
86 }
87
88 char *desc = xpc_copy_description(message);
89 os_log(OS_LOG_DEFAULT, "received xpc message: %s", desc);
90 free(desc);
91
92 @autoreleasepool {
93 handler(message);
94 }
95 });
96 xpc_connection_activate(peer);
97 });
98 xpc_connection_activate(listener);
99 }
100
101 static void
102 handle_pairing_result(bool success, NSError *error)
103 {
104 bool actual_success = false;
105 bool standard_retry = false;
106 bool ids_retry = false;
107
108 if (success) {
109 actual_success = true;
110 } else {
111 if ([error.domain isEqualToString:OTPairingErrorDomain]) {
112 switch (error.code) {
113 /* AlreadyIn: Treat like success; unregister all retries. */
114 case OTPairingErrorTypeAlreadyIn:
115 actual_success = true;
116 break;
117 /* Busy: In progress. Do nothing, leave any configured retries. */
118 case OTPairingErrorTypeBusy:
119 break;
120 /* IDS error. Set up IDS retry _and_ standard retry. */
121 case OTPairingErrorTypeIDS:
122 standard_retry = true;
123 ids_retry = true;
124 break;
125 /* Other error, standard retry. */
126 default:
127 standard_retry = true;
128 break;
129 }
130 } else {
131 standard_retry = true;
132 }
133 }
134
135 if (actual_success) {
136 pairing_retry_unregister();
137 ids_retry_enable(false);
138 } else {
139 if (standard_retry) {
140 pairing_retry_register(false);
141 }
142 if (ids_retry) {
143 ids_retry_enable(true);
144 }
145 }
146 }
147
148 static void
149 pairing_retry_register(bool checkin)
150 {
151 xpc_object_t criteria;
152
153 if (checkin) {
154 criteria = XPC_ACTIVITY_CHECK_IN;
155 } else {
156 os_log(OS_LOG_DEFAULT, "scheduling pairing retry");
157
158 pairing_retry_unregister();
159
160 criteria = xpc_dictionary_create(NULL, NULL, 0);
161 xpc_dictionary_set_string(criteria, XPC_ACTIVITY_PRIORITY, XPC_ACTIVITY_PRIORITY_MAINTENANCE);
162 xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_INTERVAL, OTPairingXPCActivityInterval);
163 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REPEATING, true);
164 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_ALLOW_BATTERY, true);
165 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REQUIRES_CLASS_A, true);
166 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_COMMUNICATES_WITH_PAIRED_DEVICE, true);
167 }
168
169 xpc_activity_register(OTPairingXPCActivityIdentifier, criteria, ^(xpc_activity_t activity) {
170 xpc_activity_state_t state = xpc_activity_get_state(activity);
171 if (state == XPC_ACTIVITY_STATE_RUN) {
172 os_log(OS_LOG_DEFAULT, "triggered pairing attempt via XPC Activity");
173 OTPairingService *service = [OTPairingService sharedService];
174 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
175 if (success) {
176 os_log(OS_LOG_DEFAULT, "Pairing retry succeeded");
177 pairing_retry_unregister();
178 } else {
179 os_log(OS_LOG_DEFAULT, "Pairing retry failed: %@", error);
180 // Activity repeats...
181 }
182 }];
183 }
184 });
185 }
186
187 static void
188 pairing_retry_unregister(void)
189 {
190 xpc_activity_unregister(OTPairingXPCActivityIdentifier);
191 }
192
193 static void
194 ids_retry_init(void)
195 {
196 xpc_set_event_stream_handler("com.apple.notifyd.matching", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(xpc_object_t event) {
197 const char *name;
198 uint64_t state;
199
200 name = xpc_dictionary_get_string(event, XPC_EVENT_KEY_NAME);
201 if (strcmp(name, OTPairingXPCEventIDSDeviceState) != 0) {
202 return;
203 }
204
205 state = xpc_dictionary_get_uint64(event, "_State");
206 if ((state & kIDSDeviceStatePropertiesIsNearby) && (state & kIDSDeviceStatePropertiesIsConnected)) {
207 OTPairingService *service = [OTPairingService sharedService];
208 os_log(OS_LOG_DEFAULT, "IDS paired device is connected, retrying");
209 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
210 if (success) {
211 os_log(OS_LOG_DEFAULT, "IDS notification retry succeeded");
212 } else {
213 os_log(OS_LOG_DEFAULT, "IDS notification retry failed: %@", error);
214 }
215 handle_pairing_result(success, error);
216 }];
217 }
218 });
219 }
220
221 static void
222 ids_retry_enable(bool enable)
223 {
224 const char *notification;
225 xpc_object_t dict;
226
227 @autoreleasepool {
228 if (enable) {
229 notification = [OTPairingService sharedService].pairedDeviceNotificationName.UTF8String;
230 if (notification != NULL) {
231 dict = xpc_dictionary_create(NULL, NULL, 0);
232 xpc_dictionary_set_string(dict, "Notification", notification);
233 }
234 } else {
235 dict = NULL;
236 }
237
238 xpc_set_event("com.apple.notifyd.matching", OTPairingXPCEventIDSDeviceState, dict);
239 }
240 }
241 #endif /* TARGET_OS_WATCH */