xpc: add support for logging over XPC channels
[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_connection_cancel(entry->conn);
75 xpc_release(entry->conn);
76 free(entry);
77 }
78
79 /**
80 * Remove an entry for a given XPC connection
81 */
82 static void remove_conn(private_xpc_channels_t *this, xpc_connection_t conn)
83 {
84 enumerator_t *enumerator;
85 entry_t *entry;
86
87 this->lock->write_lock(this->lock);
88 enumerator = this->channels->create_enumerator(this->channels);
89 while (enumerator->enumerate(enumerator, NULL, &entry))
90 {
91 if (xpc_equal(entry->conn, conn))
92 {
93 this->channels->remove(this->channels, enumerator);
94 destroy_entry(entry);
95 break;
96 }
97 }
98 enumerator->destroy(enumerator);
99 this->lock->unlock(this->lock);
100 }
101
102 /**
103 * Handle a request message from App
104 */
105 static void handle(private_xpc_channels_t *this, xpc_object_t request)
106 {
107 /* TODO: */
108 }
109
110 METHOD(xpc_channels_t, add, void,
111 private_xpc_channels_t *this, xpc_connection_t conn, u_int32_t ike_sa)
112 {
113 entry_t *entry;
114
115 INIT(entry,
116 .conn = conn,
117 .sa = ike_sa,
118 .logger = xpc_logger_create(conn),
119 );
120
121 xpc_connection_set_event_handler(entry->conn, ^(xpc_object_t event) {
122
123 if (event == XPC_ERROR_CONNECTION_INVALID ||
124 event == XPC_ERROR_CONNECTION_INTERRUPTED)
125 {
126 remove_conn(this, conn);
127 }
128 else
129 {
130 handle(this, event);
131 }
132 });
133
134 entry->logger->set_ike_sa(entry->logger, entry->sa);
135 charon->bus->add_logger(charon->bus, &entry->logger->logger);
136
137 this->lock->write_lock(this->lock);
138 this->channels->put(this->channels, (void*)entry->sa, entry);
139 this->lock->unlock(this->lock);
140
141 xpc_connection_resume(conn);
142 }
143
144 METHOD(listener_t, ike_rekey, bool,
145 private_xpc_channels_t *this, ike_sa_t *old, ike_sa_t *new)
146 {
147 entry_t *entry;
148 uintptr_t sa;
149
150 sa = old->get_unique_id(old);
151 this->lock->write_lock(this->lock);
152 entry = this->channels->remove(this->channels, (void*)sa);
153 if (entry)
154 {
155 entry->sa = new->get_unique_id(new);
156 entry->logger->set_ike_sa(entry->logger, entry->sa);
157 this->channels->put(this->channels, (void*)entry->sa, entry);
158 }
159 this->lock->unlock(this->lock);
160
161 return TRUE;
162 }
163
164 METHOD(listener_t, ike_updown, bool,
165 private_xpc_channels_t *this, ike_sa_t *ike_sa, bool up)
166 {
167 xpc_object_t msg;
168 entry_t *entry;
169 uintptr_t sa;
170
171 sa = ike_sa->get_unique_id(ike_sa);
172 if (up)
173 {
174 this->lock->read_lock(this->lock);
175 entry = this->channels->get(this->channels, (void*)sa);
176 if (entry)
177 {
178 msg = xpc_dictionary_create(NULL, NULL, 0);
179 xpc_dictionary_set_string(msg, "type", "event");
180 xpc_dictionary_set_string(msg, "event", "up");
181 xpc_connection_send_message(entry->conn, msg);
182 xpc_release(msg);
183 }
184 this->lock->unlock(this->lock);
185 }
186 else
187 {
188 this->lock->write_lock(this->lock);
189 entry = this->channels->remove(this->channels, (void*)sa);
190 this->lock->unlock(this->lock);
191 if (entry)
192 {
193 msg = xpc_dictionary_create(NULL, NULL, 0);
194 xpc_dictionary_set_string(msg, "type", "event");
195 xpc_dictionary_set_string(msg, "event", "down");
196 xpc_connection_send_message(entry->conn, msg);
197 xpc_release(msg);
198 xpc_connection_send_barrier(entry->conn, ^() {
199 destroy_entry(entry);
200 });
201 }
202 }
203 return TRUE;
204 }
205
206 /**
207 * Query password from App using XPC channel
208 */
209 static shared_key_t *query_password(xpc_connection_t conn, identification_t *id)
210 {
211 char buf[128], *password;
212 xpc_object_t request, response;
213 shared_key_t *shared = NULL;
214
215 request = xpc_dictionary_create(NULL, NULL, 0);
216 xpc_dictionary_set_string(request, "type", "rpc");
217 xpc_dictionary_set_string(request, "rpc", "get_password");
218 snprintf(buf, sizeof(buf), "%Y", id);
219 xpc_dictionary_set_string(request, "username", buf);
220
221 response = xpc_connection_send_message_with_reply_sync(conn, request);
222 xpc_release(request);
223 if (xpc_get_type(response) == XPC_TYPE_DICTIONARY)
224 {
225 password = (char*)xpc_dictionary_get_string(response, "password");
226 shared = shared_key_create(SHARED_EAP,
227 chunk_clone(chunk_from_str(password)));
228 }
229 xpc_release(response);
230 return shared;
231 }
232
233 /**
234 * Password query callback
235 */
236 static shared_key_t* password_cb(private_xpc_channels_t *this,
237 shared_key_type_t type,
238 identification_t *me, identification_t *other,
239 id_match_t *match_me, id_match_t *match_other)
240 {
241 shared_key_t *shared = NULL;
242 ike_sa_t *ike_sa;
243 entry_t *entry;
244 u_int32_t sa;
245
246 switch (type)
247 {
248 case SHARED_EAP:
249 break;
250 default:
251 return NULL;
252 }
253 ike_sa = charon->bus->get_sa(charon->bus);
254 if (ike_sa)
255 {
256 sa = ike_sa->get_unique_id(ike_sa);
257 this->lock->read_lock(this->lock);
258 entry = this->channels->get(this->channels, (void*)sa);
259 if (entry && !entry->passworded)
260 {
261 entry->passworded = TRUE;
262
263 shared = query_password(entry->conn, me);
264 if (shared)
265 {
266 if (match_me)
267 {
268 *match_me = ID_MATCH_PERFECT;
269 }
270 if (match_other)
271 {
272 *match_other = ID_MATCH_PERFECT;
273 }
274 }
275 }
276 this->lock->unlock(this->lock);
277 }
278 return shared;
279 }
280
281 METHOD(xpc_channels_t, destroy, void,
282 private_xpc_channels_t *this)
283 {
284 enumerator_t *enumerator;
285 entry_t *entry;
286
287 lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
288 this->creds->destroy(this->creds);
289
290 enumerator = this->channels->create_enumerator(this->channels);
291 while (enumerator->enumerate(enumerator, NULL, &entry))
292 {
293 destroy_entry(entry);
294 }
295 enumerator->destroy(enumerator);
296
297 this->channels->destroy(this->channels);
298 this->lock->destroy(this->lock);
299 free(this);
300 }
301
302 /**
303 * See header
304 */
305 xpc_channels_t *xpc_channels_create()
306 {
307 private_xpc_channels_t *this;
308
309 INIT(this,
310 .public = {
311 .listener = {
312 .ike_updown = _ike_updown,
313 .ike_rekey = _ike_rekey,
314 },
315 .add = _add,
316 .destroy = _destroy,
317 },
318 .channels = hashtable_create(hashtable_hash_ptr,
319 hashtable_equals_ptr, 4),
320 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
321 );
322
323 this->creds = callback_cred_create_shared(
324 (callback_cred_shared_cb_t)password_cb, this);
325 lib->credmgr->add_set(lib->credmgr, &this->creds->set);
326
327 return &this->public;
328 }