]> git.saurik.com Git - apple/security.git/blob - keychain/otpaird/main.m
ec1ed214c74f197e3c1be59e79c40afd3a818f94
[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 <NanoRegistry/NanoRegistry.h>
7 #import <xpc/private.h>
8 #endif /* TARGET_OS_WATCH */
9
10 #import "OTPairingService.h"
11 #import "OTPairingConstants.h"
12
13 #if TARGET_OS_WATCH
14 static void create_xpc_listener(xpc_handler_t handler);
15 static bool should_retry(NSError *error);
16 static void pairing_retry_register(bool checkin);
17 static void pairing_retry_unregister(void);
18 #endif /* TARGET_OS_WATCH */
19
20 int
21 main()
22 {
23 static OTPairingService *service;
24
25 @autoreleasepool {
26 service = [OTPairingService sharedService];
27 }
28
29 #if TARGET_OS_WATCH
30 /* Check in; handle a possibly-pending retry. */
31 pairing_retry_register(true);
32
33 create_xpc_listener(^(xpc_object_t message) {
34 xpc_object_t reply;
35
36 reply = xpc_dictionary_create_reply(message);
37
38 /* Received an explicit pairing request; remove retry activity if one exists. */
39 pairing_retry_unregister();
40
41 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
42 xpc_connection_t connection;
43
44 if (success) {
45 os_log(OS_LOG_DEFAULT, "xpc-initiated pairing succeeded");
46 } else {
47 os_log(OS_LOG_DEFAULT, "xpc-initiated pairing failed: %@", error);
48 }
49
50 xpc_dictionary_set_bool(reply, OTPairingXPCKeySuccess, success);
51 if (error) {
52 NSData *errdata = [SecXPCHelper encodedDataFromError:error];
53 xpc_dictionary_set_data(reply, OTPairingXPCKeyError, errdata.bytes, errdata.length);
54 }
55 connection = xpc_dictionary_get_remote_connection(reply);
56 xpc_connection_send_message(connection, reply);
57
58 // Retry on failure - unless we were already in
59 if (!success && should_retry(error)) {
60 pairing_retry_register(false);
61 }
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 bool
102 should_retry(NSError *error)
103 {
104 bool retry;
105
106 if ([error.domain isEqualToString:OTPairingErrorDomain]) {
107 switch (error.code) {
108 case OTPairingErrorTypeAlreadyIn:
109 case OTPairingErrorTypeBusy:
110 retry = false;
111 break;
112 default:
113 retry = true;
114 break;
115 }
116 } else {
117 retry = true;
118 }
119
120 return retry;
121 }
122
123 static void
124 pairing_retry_register(bool checkin)
125 {
126 xpc_object_t criteria;
127
128 if (checkin) {
129 criteria = XPC_ACTIVITY_CHECK_IN;
130 } else {
131 os_log(OS_LOG_DEFAULT, "scheduling pairing retry");
132
133 pairing_retry_unregister();
134
135 criteria = xpc_dictionary_create(NULL, NULL, 0);
136 xpc_dictionary_set_string(criteria, XPC_ACTIVITY_PRIORITY, XPC_ACTIVITY_PRIORITY_MAINTENANCE);
137 xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_INTERVAL, OTPairingXPCActivityInterval);
138 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REPEATING, true);
139 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_ALLOW_BATTERY, true);
140 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REQUIRES_CLASS_A, true);
141 xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_COMMUNICATES_WITH_PAIRED_DEVICE, true);
142 }
143
144 xpc_activity_register(OTPairingXPCActivityIdentifier, criteria, ^(xpc_activity_t activity) {
145 xpc_activity_state_t state = xpc_activity_get_state(activity);
146 if (state == XPC_ACTIVITY_STATE_RUN) {
147 os_log(OS_LOG_DEFAULT, "triggered pairing attempt via XPC Activity");
148 OTPairingService *service = [OTPairingService sharedService];
149 [service initiatePairingWithCompletion:^(bool success, NSError *error) {
150 if (success) {
151 os_log(OS_LOG_DEFAULT, "Pairing retry succeeded");
152 pairing_retry_unregister();
153 } else {
154 os_log(OS_LOG_DEFAULT, "Pairing retry failed: %@", error);
155 // Activity repeats...
156 }
157 }];
158 }
159 });
160 }
161
162 static void
163 pairing_retry_unregister(void)
164 {
165 xpc_activity_unregister(OTPairingXPCActivityIdentifier);
166 }
167 #endif /* TARGET_OS_WATCH */