ported NM plugin to upstream NetworkManager changes
[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 bus_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 /* there is no way to do this cleanly, as the address range may
53 * be anything else but a subnet. We use from_addr as subnet
54 * and try to calculate a usable subnet mask.
55 */
56 int byte, bit, net;
57 bool found = FALSE;
58 chunk_t from, to;
59 size_t size = (ts->get_type(ts) == TS_IPV4_ADDR_RANGE) ? 4 : 16;
60
61 from = ts->get_from_address(ts);
62 to = ts->get_to_address(ts);
63
64 *mask = (size * 8);
65 /* go trough all bits of the addresses, beginning in the front.
66 * as long as they are equal, the subnet gets larger
67 */
68 for (byte = 0; byte < size; byte++)
69 {
70 for (bit = 7; bit >= 0; bit--)
71 {
72 if ((1<<bit & from.ptr[byte]) != (1<<bit & to.ptr[byte]))
73 {
74 *mask = ((7 - bit) + (byte * 8));
75 found = TRUE;
76 break;
77 }
78 }
79 if (found)
80 {
81 break;
82 }
83 }
84 net = *(u_int32_t*)from.ptr;
85 chunk_free(&from);
86 chunk_free(&to);
87 return net;
88 }
89
90 /**
91 * signal IPv4 config to NM, set connection as established
92 */
93 static void signal_ipv4_config(NMVPNPlugin *plugin, child_sa_t *child_sa)
94 {
95 linked_list_t *list;
96 traffic_selector_t *ts = NULL;
97 enumerator_t *enumerator;
98
99 list = child_sa->get_traffic_selectors(child_sa, FALSE);
100 enumerator = list->create_enumerator(list);
101 while (enumerator->enumerate(enumerator, &ts))
102 {
103 GValue *val;
104 GHashTable *config;
105 u_int8_t mask;
106
107 config = g_hash_table_new(g_str_hash, g_str_equal);
108
109 val = g_slice_new0(GValue);
110 g_value_init(val, G_TYPE_UINT);
111 g_value_set_uint(val, ts2subnet(ts, &mask));
112 g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
113
114 val = g_slice_new0(GValue);
115 g_value_init(val, G_TYPE_UINT);
116 g_value_set_uint(val, mask);
117 g_hash_table_insert(config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
118
119 nm_vpn_plugin_set_ip4_config(plugin, config);
120 }
121 enumerator->destroy(enumerator);
122 }
123
124 /**
125 * Bus listen function to wait for SA establishing
126 */
127 bool listen_bus(bus_listener_t *listener, signal_t signal, level_t level,
128 int thread, ike_sa_t *ike_sa, void *data,
129 char* format, va_list args)
130 {
131 NMStrongswanPluginPrivate *private = (NMStrongswanPluginPrivate*)listener;
132
133 if (private->ike_sa == ike_sa)
134 {
135 switch (signal)
136 {
137 case CHD_UP_SUCCESS:
138 if (data)
139 {
140 signal_ipv4_config(private->plugin, (child_sa_t*)data);
141 return FALSE;
142 }
143 /* FALL */
144 case IKE_UP_FAILED:
145 case CHD_UP_FAILED:
146 /* TODO: NM does not handle this failure!?
147 nm_vpn_plugin_failure(private->plugin,
148 NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED); */
149 nm_vpn_plugin_set_state(private->plugin,
150 NM_VPN_SERVICE_STATE_STOPPED);
151 return FALSE;
152 default:
153 break;
154 }
155 }
156 return TRUE;
157 }
158
159 /**
160 * Connect function called from NM via DBUS
161 */
162 static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection,
163 GError **err)
164 {
165 nm_creds_t *creds;
166 NMSettingVPN *settings;
167 identification_t *user = NULL, *gateway;
168 char *address, *str;
169 bool virtual, encap, ipcomp;
170 ike_cfg_t *ike_cfg;
171 peer_cfg_t *peer_cfg;
172 child_cfg_t *child_cfg;
173 traffic_selector_t *ts;
174 ike_sa_t *ike_sa;
175 auth_info_t *auth;
176 auth_class_t auth_class = AUTH_CLASS_EAP;
177 certificate_t *cert = NULL;
178 bool agent = FALSE;
179
180 /**
181 * Read parameters
182 */
183 settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
184 NM_TYPE_SETTING_VPN));
185
186 DBG4(DBG_CFG, "received NetworkManager connection: %s",
187 nm_setting_to_string(NM_SETTING(settings)));
188 address = g_hash_table_lookup(settings->data, "address");
189 if (!address || !*address)
190 {
191 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
192 "Gateway address missing.");
193 return FALSE;
194 }
195 str = g_hash_table_lookup(settings->data, "virtual");
196 virtual = str && streq(str, "yes");
197 str = g_hash_table_lookup(settings->data, "encap");
198 encap = str && streq(str, "yes");
199 str = g_hash_table_lookup(settings->data, "ipcomp");
200 ipcomp = str && streq(str, "yes");
201 str = g_hash_table_lookup(settings->data, "method");
202 if (str)
203 {
204 if (streq(str, "psk"))
205 {
206 auth_class = AUTH_CLASS_PSK;
207 }
208 else if (streq(str, "agent"))
209 {
210 auth_class = AUTH_CLASS_PUBKEY;
211 agent = TRUE;
212 }
213 else if (streq(str, "key"))
214 {
215 auth_class = AUTH_CLASS_PUBKEY;
216 }
217 }
218
219 /**
220 * Register credentials
221 */
222 creds = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->creds;
223 creds->clear(creds);
224
225 /* gateway cert */
226 str = g_hash_table_lookup(settings->data, "certificate");
227 if (str)
228 {
229 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
230 BUILD_FROM_FILE, str, BUILD_END);
231 creds->set_certificate(creds, cert);
232 }
233 if (!cert)
234 {
235 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
236 "Loading gateway certificate failed.");
237 return FALSE;
238 }
239 gateway = cert->get_subject(cert);
240
241 if (auth_class == AUTH_CLASS_EAP)
242 {
243 /* username/password authentication ... */
244 str = g_hash_table_lookup(settings->data, "user");
245 if (str)
246 {
247 user = identification_create_from_string(str);
248 str = g_hash_table_lookup(settings->secrets, "password");
249 creds->set_username_password(creds, user, str);
250 }
251 }
252
253 if (auth_class == AUTH_CLASS_PUBKEY)
254 {
255 /* ... or certificate/private key authenitcation */
256 str = g_hash_table_lookup(settings->data, "usercert");
257 if (str)
258 {
259 public_key_t *public;
260 private_key_t *private = NULL;
261
262 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
263 BUILD_FROM_FILE, str, BUILD_END);
264
265 /* try agent */
266 str = g_hash_table_lookup(settings->secrets, "agent");
267 if (agent && str && cert)
268 {
269 public = cert->get_public_key(cert);
270 if (public)
271 {
272 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
273 public->get_type(public),
274 BUILD_AGENT_SOCKET, str,
275 BUILD_PUBLIC_KEY, public,
276 BUILD_END);
277 public->destroy(public);
278 }
279 }
280 /* ... or key file */
281 str = g_hash_table_lookup(settings->data, "userkey");
282 if (!agent && str && cert)
283 {
284 chunk_t secret, chunk;
285 bool pgp = FALSE;
286
287 secret.ptr = g_hash_table_lookup(settings->secrets, "password");
288 if (secret.ptr)
289 {
290 secret.len = strlen(secret.ptr);
291 }
292 if (pem_asn1_load_file(str, &secret, &chunk, &pgp))
293 {
294 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
295 KEY_RSA, BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
296 free(chunk.ptr);
297 }
298 }
299 if (private)
300 {
301 user = cert->get_subject(cert);
302 user = user->clone(user);
303 creds->set_cert_and_key(creds, cert, private);
304 }
305 else
306 {
307 DESTROY_IF(cert);
308 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
309 "Loading user certificate/private key failed.");
310 return FALSE;
311 }
312 }
313 }
314
315 if (!user)
316 {
317 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
318 "Configuration parameters missing.");
319 return FALSE;
320 }
321
322 /**
323 * Set up configurations
324 */
325 ike_cfg = ike_cfg_create(TRUE, encap, "0.0.0.0", address);
326 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
327 peer_cfg = peer_cfg_create(CONFIG_NAME, 2, ike_cfg,
328 user, gateway->clone(gateway),
329 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
330 36000, 0, /* rekey 10h, reauth none */
331 600, 600, /* jitter, over 10min */
332 TRUE, 0, /* mobike, DPD */
333 virtual ? host_create_from_string("0.0.0.0", 0) : NULL,
334 NULL, FALSE, NULL, NULL); /* pool, mediation */
335 auth = peer_cfg->get_auth(peer_cfg);
336 auth->add_item(auth, AUTHN_AUTH_CLASS, &auth_class);
337 child_cfg = child_cfg_create(CONFIG_NAME,
338 10800, 10200, /* lifetime 3h, rekey 2h50min */
339 300, /* jitter 5min */
340 NULL, TRUE, MODE_TUNNEL, /* updown, hostaccess */
341 ACTION_NONE, ACTION_RESTART, ipcomp);
342 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
343 ts = traffic_selector_create_dynamic(0, 0, 65535);
344 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
345 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
346 "0.0.0.0", 0,
347 "255.255.255.255", 65535);
348 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
349 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
350
351 /**
352 * Start to initiate
353 */
354 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
355 peer_cfg);
356 if (!ike_sa->get_peer_cfg(ike_sa))
357 {
358 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
359 }
360 else
361 {
362 peer_cfg->destroy(peer_cfg);
363 }
364 if (ike_sa->initiate(ike_sa, child_cfg) != SUCCESS)
365 {
366 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
367
368 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
369 "Initiating failed.");
370 return FALSE;
371 }
372
373 /**
374 * Register listener
375 */
376 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->ike_sa = ike_sa;
377 charon->bus->add_listener(charon->bus,
378 &NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->listener);
379 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
380 return TRUE;
381 }
382
383 /**
384 * NeedSecrets called from NM via DBUS
385 */
386 static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
387 char **setting_name, GError **error)
388 {
389 NMSettingVPN *settings;
390 char *method, *path;
391 chunk_t secret = chunk_empty, key;
392 bool pgp = FALSE;
393
394 settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
395 NM_TYPE_SETTING_VPN));
396 method = g_hash_table_lookup(settings->data, "method");
397 if (method)
398 {
399 if (streq(method, "eap"))
400 {
401 if (g_hash_table_lookup(settings->secrets, "password"))
402 {
403 return FALSE;
404 }
405 }
406 else if (streq(method, "agent"))
407 {
408 if (g_hash_table_lookup(settings->secrets, "agent"))
409 {
410 return FALSE;
411 }
412 }
413 else if (streq(method, "key"))
414 {
415 path = g_hash_table_lookup(settings->data, "userkey");
416 if (path)
417 {
418 secret.ptr = g_hash_table_lookup(settings->secrets, "password");
419 if (secret.ptr)
420 {
421 secret.len = strlen(secret.ptr);
422 }
423 if (pem_asn1_load_file(path, &secret, &key, &pgp))
424 {
425 free(key.ptr);
426 return FALSE;
427 }
428 }
429 }
430 }
431 *setting_name = NM_SETTING_VPN_SETTING_NAME;
432 return TRUE;
433 }
434
435 /**
436 * Disconnect called from NM via DBUS
437 */
438 static gboolean disconnect(NMVPNPlugin *plugin, GError **err)
439 {
440 enumerator_t *enumerator;
441 ike_sa_t *ike_sa;
442 u_int id;
443
444 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
445 while (enumerator->enumerate(enumerator, &ike_sa))
446 {
447 if (streq(CONFIG_NAME, ike_sa->get_name(ike_sa)))
448 {
449 id = ike_sa->get_unique_id(ike_sa);
450 enumerator->destroy(enumerator);
451 charon->controller->terminate_ike(charon->controller, id,
452 controller_cb_empty, NULL);
453 return TRUE;
454 }
455 }
456 enumerator->destroy(enumerator);
457 return FALSE;
458 }
459
460 /**
461 * Initializer
462 */
463 static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin)
464 {
465 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->plugin = NM_VPN_PLUGIN(plugin);
466 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->listener.signal = listen_bus;
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