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