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