reintegrated bus-refactoring branch
[strongswan.git] / src / charon / plugins / nm / nm_service.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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 * $Id$
16 */
17
18 #include <nm-setting-vpn.h>
19 #include "nm_service.h"
20
21 #include <daemon.h>
22 #include <asn1/pem.h>
23 #include <utils/host.h>
24 #include <utils/identification.h>
25 #include <config/peer_cfg.h>
26
27 #include <stdio.h>
28
29 #define CONFIG_NAME "NetworkManager"
30
31 G_DEFINE_TYPE(NMStrongswanPlugin, nm_strongswan_plugin, NM_TYPE_VPN_PLUGIN)
32
33 /**
34 * Private data of NMStrongswanPlugin
35 */
36 typedef struct {
37 listener_t listener;
38 ike_sa_t *ike_sa;
39 NMVPNPlugin *plugin;
40 nm_creds_t *creds;
41 } NMStrongswanPluginPrivate;
42
43 #define NM_STRONGSWAN_PLUGIN_GET_PRIVATE(o) \
44 (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
45 NM_TYPE_STRONGSWAN_PLUGIN, NMStrongswanPluginPrivate))
46
47 /**
48 * convert a traffic selector address range to subnet and its mask.
49 */
50 static u_int ts2subnet(traffic_selector_t* ts, u_int8_t *mask)
51 {
52 u_int net;
53 host_t *net_host;
54 chunk_t net_chunk;
55
56 ts->to_subnet(ts, &net_host, mask);
57 net_chunk = net_host->get_address(net_host);
58 net = *(u_int32_t*)net_chunk.ptr;
59 net_host->destroy(net_host);
60 return net;
61 }
62
63 /**
64 * signal IPv4 config to NM, set connection as established
65 */
66 static void signal_ipv4_config(NMVPNPlugin *plugin, child_sa_t *child_sa)
67 {
68 linked_list_t *list;
69 traffic_selector_t *ts = NULL;
70 enumerator_t *enumerator;
71
72 list = child_sa->get_traffic_selectors(child_sa, FALSE);
73 enumerator = list->create_enumerator(list);
74 while (enumerator->enumerate(enumerator, &ts))
75 {
76 GValue *val;
77 GHashTable *config;
78 u_int8_t mask;
79
80 config = g_hash_table_new(g_str_hash, g_str_equal);
81
82 val = g_slice_new0(GValue);
83 g_value_init(val, G_TYPE_UINT);
84 g_value_set_uint(val, ts2subnet(ts, &mask));
85 g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
86
87 val = g_slice_new0(GValue);
88 g_value_init(val, G_TYPE_UINT);
89 g_value_set_uint(val, mask);
90 g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
91
92 nm_vpn_plugin_set_ip4_config(plugin, config);
93 }
94 enumerator->destroy(enumerator);
95 }
96
97 /**
98 * signal failure to NM, connecting failed
99 */
100 static void signal_failure(NMVPNPlugin *plugin)
101 {
102 /* TODO: NM does not handle this failure!?
103 nm_vpn_plugin_failure(plugin, NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED); */
104 nm_vpn_plugin_set_state(plugin, NM_VPN_SERVICE_STATE_STOPPED);
105 }
106
107 /**
108 * Implementation of listener_t.ike_state_change
109 */
110 static bool ike_state_change(listener_t *listener, ike_sa_t *ike_sa,
111 ike_sa_state_t state)
112 {
113 NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener;
114
115 if (private->ike_sa == ike_sa)
116 {
117 switch (state)
118 {
119 case IKE_DESTROYING:
120 signal_failure(private->plugin);
121 return FALSE;
122 default:
123 break;
124 }
125 }
126 return TRUE;
127 }
128
129 /**
130 * Implementation of listener_t.child_state_change
131 */
132 static bool child_state_change(listener_t *listener, ike_sa_t *ike_sa,
133 child_sa_t *child_sa, child_sa_state_t state)
134 {
135 NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener;
136
137 if (private->ike_sa == ike_sa)
138 {
139 switch (state)
140 {
141 case CHILD_INSTALLED:
142 signal_ipv4_config(private->plugin, child_sa);
143 return FALSE;
144 case CHILD_DESTROYING:
145 signal_failure(private->plugin);
146 return FALSE;
147 default:
148 break;
149 }
150 }
151 return TRUE;
152 }
153
154 /**
155 * Connect function called from NM via DBUS
156 */
157 static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
158 GError **err)
159 {
160 nm_creds_t *creds;
161 NMSettingVPN *settings;
162 identification_t *user = NULL, *gateway;
163 char *address, *str;
164 bool virtual, encap, ipcomp;
165 ike_cfg_t *ike_cfg;
166 peer_cfg_t *peer_cfg;
167 child_cfg_t *child_cfg;
168 traffic_selector_t *ts;
169 ike_sa_t *ike_sa;
170 auth_info_t *auth;
171 auth_class_t auth_class = AUTH_CLASS_EAP;
172 certificate_t *cert = NULL;
173 bool agent = FALSE;
174
175 /**
176 * Read parameters
177 */
178 settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
179 NM_TYPE_SETTING_VPN));
180
181 DBG4(DBG_CFG, "received NetworkManager connection: %s",
182 nm_setting_to_string(NM_SETTING(settings)));
183 address = g_hash_table_lookup(settings->data, "address");
184 if (!address || !*address)
185 {
186 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
187 "Gateway address missing.");
188 return FALSE;
189 }
190 str = g_hash_table_lookup(settings->data, "virtual");
191 virtual = str && streq(str, "yes");
192 str = g_hash_table_lookup(settings->data, "encap");
193 encap = str && streq(str, "yes");
194 str = g_hash_table_lookup(settings->data, "ipcomp");
195 ipcomp = str && streq(str, "yes");
196 str = g_hash_table_lookup(settings->data, "method");
197 if (str)
198 {
199 if (streq(str, "psk"))
200 {
201 auth_class = AUTH_CLASS_PSK;
202 }
203 else if (streq(str, "agent"))
204 {
205 auth_class = AUTH_CLASS_PUBKEY;
206 agent = TRUE;
207 }
208 else if (streq(str, "key"))
209 {
210 auth_class = AUTH_CLASS_PUBKEY;
211 }
212 }
213
214 /**
215 * Register credentials
216 */
217 creds = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->creds;
218 creds->clear(creds);
219
220 /* gateway cert */
221 str = g_hash_table_lookup(settings->data, "certificate");
222 if (str)
223 {
224 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
225 BUILD_FROM_FILE, str, BUILD_END);
226 creds->set_certificate(creds, cert);
227 }
228 if (!cert)
229 {
230 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
231 "Loading gateway certificate failed.");
232 return FALSE;
233 }
234 gateway = cert->get_subject(cert);
235
236 if (auth_class == AUTH_CLASS_EAP)
237 {
238 /* username/password authentication ... */
239 str = g_hash_table_lookup(settings->data, "user");
240 if (str)
241 {
242 user = identification_create_from_string(str);
243 str = g_hash_table_lookup(settings->secrets, "password");
244 creds->set_username_password(creds, user, str);
245 }
246 }
247
248 if (auth_class == AUTH_CLASS_PUBKEY)
249 {
250 /* ... or certificate/private key authenitcation */
251 str = g_hash_table_lookup(settings->data, "usercert");
252 if (str)
253 {
254 public_key_t *public;
255 private_key_t *private = NULL;
256
257 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
258 BUILD_FROM_FILE, str, BUILD_END);
259
260 /* try agent */
261 str = g_hash_table_lookup(settings->secrets, "agent");
262 if (agent && str && cert)
263 {
264 public = cert->get_public_key(cert);
265 if (public)
266 {
267 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
268 public->get_type(public),
269 BUILD_AGENT_SOCKET, str,
270 BUILD_PUBLIC_KEY, public,
271 BUILD_END);
272 public->destroy(public);
273 }
274 }
275 /* ... or key file */
276 str = g_hash_table_lookup(settings->data, "userkey");
277 if (!agent && str && cert)
278 {
279 chunk_t secret, chunk;
280 bool pgp = FALSE;
281
282 secret.ptr = g_hash_table_lookup(settings->secrets, "password");
283 if (secret.ptr)
284 {
285 secret.len = strlen(secret.ptr);
286 }
287 if (pem_asn1_load_file(str, &secret, &chunk, &pgp))
288 {
289 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
290 KEY_RSA, BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
291 free(chunk.ptr);
292 }
293 }
294 if (private)
295 {
296 user = cert->get_subject(cert);
297 user = user->clone(user);
298 creds->set_cert_and_key(creds, cert, private);
299 }
300 else
301 {
302 DESTROY_IF(cert);
303 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
304 "Loading user certificate/private key failed.");
305 return FALSE;
306 }
307 }
308 }
309
310 if (!user)
311 {
312 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
313 "Configuration parameters missing.");
314 return FALSE;
315 }
316
317 /**
318 * Set up configurations
319 */
320 ike_cfg = ike_cfg_create(TRUE, encap, "0.0.0.0", address);
321 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
322 peer_cfg = peer_cfg_create(CONFIG_NAME, 2, ike_cfg,
323 user, gateway->clone(gateway),
324 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
325 36000, 0, /* rekey 10h, reauth none */
326 600, 600, /* jitter, over 10min */
327 TRUE, 0, /* mobike, DPD */
328 virtual ? host_create_from_string("0.0.0.0", 0) : NULL,
329 NULL, FALSE, NULL, NULL); /* pool, mediation */
330 auth = peer_cfg->get_auth(peer_cfg);
331 auth->add_item(auth, AUTHN_AUTH_CLASS, &auth_class);
332 child_cfg = child_cfg_create(CONFIG_NAME,
333 10800, 10200, /* lifetime 3h, rekey 2h50min */
334 300, /* jitter 5min */
335 NULL, TRUE, MODE_TUNNEL, /* updown, hostaccess */
336 ACTION_NONE, ACTION_RESTART, ipcomp);
337 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
338 ts = traffic_selector_create_dynamic(0, 0, 65535);
339 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
340 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
341 "0.0.0.0", 0,
342 "255.255.255.255", 65535);
343 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
344 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
345
346 /**
347 * Start to initiate
348 */
349 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
350 peer_cfg);
351 if (!ike_sa->get_peer_cfg(ike_sa))
352 {
353 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
354 }
355 else
356 {
357 peer_cfg->destroy(peer_cfg);
358 }
359 if (ike_sa->initiate(ike_sa, child_cfg) != SUCCESS)
360 {
361 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
362
363 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
364 "Initiating failed.");
365 return FALSE;
366 }
367
368 /**
369 * Register listener
370 */
371 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->ike_sa = ike_sa;
372 charon->bus->add_listener(charon->bus,
373 &NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->listener);
374 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
375 return TRUE;
376 }
377
378 /**
379 * NeedSecrets called from NM via DBUS
380 */
381 static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
382 char **setting_name, GError **error)
383 {
384 NMSettingVPN *settings;
385 char *method, *path;
386 chunk_t secret = chunk_empty, key;
387 bool pgp = FALSE;
388
389 settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
390 NM_TYPE_SETTING_VPN));
391 method = g_hash_table_lookup(settings->data, "method");
392 if (method)
393 {
394 if (streq(method, "eap"))
395 {
396 if (g_hash_table_lookup(settings->secrets, "password"))
397 {
398 return FALSE;
399 }
400 }
401 else if (streq(method, "agent"))
402 {
403 if (g_hash_table_lookup(settings->secrets, "agent"))
404 {
405 return FALSE;
406 }
407 }
408 else if (streq(method, "key"))
409 {
410 path = g_hash_table_lookup(settings->data, "userkey");
411 if (path)
412 {
413 secret.ptr = g_hash_table_lookup(settings->secrets, "password");
414 if (secret.ptr)
415 {
416 secret.len = strlen(secret.ptr);
417 }
418 if (pem_asn1_load_file(path, &secret, &key, &pgp))
419 {
420 free(key.ptr);
421 return FALSE;
422 }
423 }
424 }
425 }
426 *setting_name = NM_SETTING_VPN_SETTING_NAME;
427 return TRUE;
428 }
429
430 /**
431 * Disconnect called from NM via DBUS
432 */
433 static gboolean disconnect(NMVPNPlugin *plugin, GError **err)
434 {
435 enumerator_t *enumerator;
436 ike_sa_t *ike_sa;
437 u_int id;
438
439 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
440 while (enumerator->enumerate(enumerator, &ike_sa))
441 {
442 if (streq(CONFIG_NAME, ike_sa->get_name(ike_sa)))
443 {
444 id = ike_sa->get_unique_id(ike_sa);
445 enumerator->destroy(enumerator);
446 charon->controller->terminate_ike(charon->controller, id,
447 controller_cb_empty, NULL);
448 return TRUE;
449 }
450 }
451 enumerator->destroy(enumerator);
452 return FALSE;
453 }
454
455 /**
456 * Initializer
457 */
458 static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin)
459 {
460 NMStrongswanPluginPrivate *private;
461
462 private = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
463 private->plugin = NM_VPN_PLUGIN(plugin);
464 memset(&private->listener.log, 0, sizeof(listener_t));
465 private->listener.ike_state_change = ike_state_change;
466 private->listener.child_state_change = child_state_change;
467 }
468
469 /**
470 * Class constructor
471 */
472 static void nm_strongswan_plugin_class_init(
473 NMStrongswanPluginClass *strongswan_class)
474 {
475 NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS(strongswan_class);
476
477 g_type_class_add_private(G_OBJECT_CLASS(strongswan_class),
478 sizeof(NMStrongswanPluginPrivate));
479 parent_class->connect = connect_;
480 parent_class->need_secrets = need_secrets;
481 parent_class->disconnect = disconnect;
482 }
483
484 /**
485 * Object constructor
486 */
487 NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds)
488 {
489 NMStrongswanPlugin *plugin = (NMStrongswanPlugin *)g_object_new (
490 NM_TYPE_STRONGSWAN_PLUGIN,
491 NM_VPN_PLUGIN_DBUS_SERVICE_NAME, NM_DBUS_SERVICE_STRONGSWAN,
492 NULL);
493 if (plugin)
494 {
495 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->creds = creds;
496 }
497 return plugin;
498 }
499