xpc: fix some refcounting issues related to XPC connections
[strongswan.git] / src / frontends / osx / charon-xpc / xpc_channels.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "xpc_channels.h"
17 #include "xpc_logger.h"
18
19 #include <credentials/sets/callback_cred.h>
20 #include <collections/hashtable.h>
21 #include <threading/rwlock.h>
22 #include <daemon.h>
23
24 typedef struct private_xpc_channels_t private_xpc_channels_t;
25
26 /**
27 * Private data of an xpc_channels_t object.
28 */
29 struct private_xpc_channels_t {
30
31 /**
32 * Public xpc_channels_t interface.
33 */
34 xpc_channels_t public;
35
36 /**
37 * Registered channels, IKE_SA unique ID => entry_t
38 */
39 hashtable_t *channels;
40
41 /**
42 * Lock for channels list
43 */
44 rwlock_t *lock;
45
46 /**
47 * Callback credential set for passwords
48 */
49 callback_cred_t *creds;
50 };
51
52 /**
53 * Channel entry
54 */
55 typedef struct {
56 /* XPC channel to App */
57 xpc_connection_t conn;
58 /* associated IKE_SA unique identifier */
59 uintptr_t sa;
60 /* did we already ask for a password? */
61 bool passworded;
62 /* channel specific logger */
63 xpc_logger_t *logger;
64 } entry_t;
65
66 /**
67 * Clean up an entry, cancelling connection
68 */
69 static void destroy_entry(entry_t *entry)
70 {
71 charon->bus->remove_logger(charon->bus, &entry->logger->logger);
72 entry->logger->destroy(entry->logger);
73 xpc_connection_suspend(entry->conn);
74 xpc_release(entry->conn);
75 free(entry);
76 }
77
78 /**
79 * Remove an entry for a given XPC connection
80 */
81 static void remove_conn(private_xpc_channels_t *this, xpc_connection_t conn)
82 {
83 enumerator_t *enumerator;
84 entry_t *entry;
85
86 this->lock->write_lock(this->lock);
87 enumerator = this->channels->create_enumerator(this->channels);
88 while (enumerator->enumerate(enumerator, NULL, &entry))
89 {
90 if (entry->conn == conn)
91 {
92 this->channels->remove(this->channels, enumerator);
93 destroy_entry(entry);
94 break;
95 }
96 }
97 enumerator->destroy(enumerator);
98 this->lock->unlock(this->lock);
99 }
100
101 /**
102 * Handle a request message from App
103 */
104 static void handle(private_xpc_channels_t *this, xpc_object_t request)
105 {
106 /* TODO: */
107 }
108
109 METHOD(xpc_channels_t, add, void,
110 private_xpc_channels_t *this, xpc_connection_t conn, u_int32_t ike_sa)
111 {
112 entry_t *entry;
113
114 INIT(entry,
115 .conn = conn,
116 .sa = ike_sa,
117 .logger = xpc_logger_create(conn),
118 );
119
120 xpc_connection_set_event_handler(entry->conn, ^(xpc_object_t event)
121 {
122 if (event == XPC_ERROR_CONNECTION_INVALID ||
123 event == XPC_ERROR_CONNECTION_INTERRUPTED)
124 {
125 remove_conn(this, conn);
126 }
127 else if (xpc_get_type(event) == XPC_TYPE_DICTIONARY)
128 {
129 handle(this, event);
130 }
131 });
132
133 entry->logger->set_ike_sa(entry->logger, entry->sa);
134 charon->bus->add_logger(charon->bus, &entry->logger->logger);
135
136 this->lock->write_lock(this->lock);
137 this->channels->put(this->channels, (void*)entry->sa, entry);
138 this->lock->unlock(this->lock);
139
140 xpc_connection_resume(conn);
141 }
142
143 METHOD(listener_t, ike_rekey, bool,
144 private_xpc_channels_t *this, ike_sa_t *old, ike_sa_t *new)
145 {
146 entry_t *entry;
147 uintptr_t sa;
148
149 sa = old->get_unique_id(old);
150 this->lock->write_lock(this->lock);
151 entry = this->channels->remove(this->channels, (void*)sa);
152 if (entry)
153 {
154 entry->sa = new->get_unique_id(new);
155 entry->logger->set_ike_sa(entry->logger, entry->sa);
156 this->channels->put(this->channels, (void*)entry->sa, entry);
157 }
158 this->lock->unlock(this->lock);
159
160 return TRUE;
161 }
162
163 METHOD(listener_t, ike_updown, bool,
164 private_xpc_channels_t *this, ike_sa_t *ike_sa, bool up)
165 {
166 xpc_object_t msg;
167 entry_t *entry;
168 uintptr_t sa;
169
170 sa = ike_sa->get_unique_id(ike_sa);
171 if (up)
172 {
173 this->lock->read_lock(this->lock);
174 entry = this->channels->get(this->channels, (void*)sa);
175 if (entry)
176 {
177 msg = xpc_dictionary_create(NULL, NULL, 0);
178 xpc_dictionary_set_string(msg, "type", "event");
179 xpc_dictionary_set_string(msg, "event", "up");
180 xpc_connection_send_message(entry->conn, msg);
181 xpc_release(msg);
182 }
183 this->lock->unlock(this->lock);
184 }
185 else
186 {
187 this->lock->write_lock(this->lock);
188 entry = this->channels->remove(this->channels, (void*)sa);
189 this->lock->unlock(this->lock);
190 if (entry)
191 {
192 msg = xpc_dictionary_create(NULL, NULL, 0);
193 xpc_dictionary_set_string(msg, "type", "event");
194 xpc_dictionary_set_string(msg, "event", "down");
195 xpc_connection_send_message(entry->conn, msg);
196 xpc_release(msg);
197 xpc_connection_send_barrier(entry->conn, ^() {
198 destroy_entry(entry);
199 });
200 }
201 }
202 return TRUE;
203 }
204
205 /**
206 * Query password from App using XPC channel
207 */
208 static shared_key_t *query_password(xpc_connection_t conn, identification_t *id)
209 {
210 char buf[128], *password;
211 xpc_object_t request, response;
212 shared_key_t *shared = NULL;
213
214 request = xpc_dictionary_create(NULL, NULL, 0);
215 xpc_dictionary_set_string(request, "type", "rpc");
216 xpc_dictionary_set_string(request, "rpc", "get_password");
217 snprintf(buf, sizeof(buf), "%Y", id);
218 xpc_dictionary_set_string(request, "username", buf);
219
220 response = xpc_connection_send_message_with_reply_sync(conn, request);
221 xpc_release(request);
222 if (xpc_get_type(response) == XPC_TYPE_DICTIONARY)
223 {
224 password = (char*)xpc_dictionary_get_string(response, "password");
225 if (password)
226 {
227 shared = shared_key_create(SHARED_EAP,
228 chunk_clone(chunk_from_str(password)));
229 }
230 }
231 xpc_release(response);
232 return shared;
233 }
234
235 /**
236 * Password query callback
237 */
238 static shared_key_t* password_cb(private_xpc_channels_t *this,
239 shared_key_type_t type,
240 identification_t *me, identification_t *other,
241 id_match_t *match_me, id_match_t *match_other)
242 {
243 shared_key_t *shared = NULL;
244 ike_sa_t *ike_sa;
245 entry_t *entry;
246 u_int32_t sa;
247
248 switch (type)
249 {
250 case SHARED_EAP:
251 break;
252 default:
253 return NULL;
254 }
255 ike_sa = charon->bus->get_sa(charon->bus);
256 if (ike_sa)
257 {
258 sa = ike_sa->get_unique_id(ike_sa);
259 this->lock->read_lock(this->lock);
260 entry = this->channels->get(this->channels, (void*)sa);
261 if (entry && !entry->passworded)
262 {
263 entry->passworded = TRUE;
264
265 shared = query_password(entry->conn, me);
266 if (shared)
267 {
268 if (match_me)
269 {
270 *match_me = ID_MATCH_PERFECT;
271 }
272 if (match_other)
273 {
274 *match_other = ID_MATCH_PERFECT;
275 }
276 }
277 }
278 this->lock->unlock(this->lock);
279 }
280 return shared;
281 }
282
283 METHOD(xpc_channels_t, destroy, void,
284 private_xpc_channels_t *this)
285 {
286 lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
287 this->creds->destroy(this->creds);
288 this->channels->destroy(this->channels);
289 this->lock->destroy(this->lock);
290 free(this);
291 }
292
293 /**
294 * See header
295 */
296 xpc_channels_t *xpc_channels_create()
297 {
298 private_xpc_channels_t *this;
299
300 INIT(this,
301 .public = {
302 .listener = {
303 .ike_updown = _ike_updown,
304 .ike_rekey = _ike_rekey,
305 },
306 .add = _add,
307 .destroy = _destroy,
308 },
309 .channels = hashtable_create(hashtable_hash_ptr,
310 hashtable_equals_ptr, 4),
311 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
312 );
313
314 this->creds = callback_cred_create_shared(
315 (callback_cred_shared_cb_t)password_cb, this);
316 lib->credmgr->add_set(lib->credmgr, &this->creds->set);
317
318 return &this->public;
319 }