2 * Copyright (C) 2008-2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include <nm-setting-vpn.h>
17 #include <nm-setting-connection.h>
18 #include "nm_service.h"
21 #include <utils/host.h>
22 #include <utils/identification.h>
23 #include <config/peer_cfg.h>
24 #include <credentials/certificates/x509.h>
28 G_DEFINE_TYPE(NMStrongswanPlugin
, nm_strongswan_plugin
, NM_TYPE_VPN_PLUGIN
)
31 * Private data of NMStrongswanPlugin
34 /* implements bus listener interface */
36 /* IKE_SA we are listening on */
38 /* backref to public plugin */
40 /* credentials to use for authentication */
42 /* attribute handler for DNS/NBNS server information */
43 nm_handler_t
*handler
;
44 /* name of the connection */
46 } NMStrongswanPluginPrivate
;
48 #define NM_STRONGSWAN_PLUGIN_GET_PRIVATE(o) \
49 (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
50 NM_TYPE_STRONGSWAN_PLUGIN, NMStrongswanPluginPrivate))
53 * convert enumerated handler chunks to a UINT_ARRAY GValue
55 static GValue
* handler_to_val(nm_handler_t
*handler
,
56 configuration_attribute_type_t type
)
60 enumerator_t
*enumerator
;
63 enumerator
= handler
->create_enumerator(handler
, type
);
64 array
= g_array_new (FALSE
, TRUE
, sizeof (guint32
));
65 while (enumerator
->enumerate(enumerator
, &chunk
))
67 g_array_append_val (array
, *(u_int32_t
*)chunk
.ptr
);
69 enumerator
->destroy(enumerator
);
70 val
= g_slice_new0 (GValue
);
71 g_value_init (val
, DBUS_TYPE_G_UINT_ARRAY
);
72 g_value_set_boxed (val
, array
);
78 * signal IPv4 config to NM, set connection as established
80 static void signal_ipv4_config(NMVPNPlugin
*plugin
,
81 ike_sa_t
*ike_sa
, child_sa_t
*child_sa
)
86 nm_handler_t
*handler
;
88 config
= g_hash_table_new(g_str_hash
, g_str_equal
);
89 me
= ike_sa
->get_my_host(ike_sa
);
90 other
= ike_sa
->get_other_host(ike_sa
);
91 handler
= NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin
)->handler
;
93 /* NM requires a tundev, but netkey does not use one. Passing an invalid
94 * iface makes NM complain, but it accepts it without fiddling on eth0. */
95 val
= g_slice_new0 (GValue
);
96 g_value_init (val
, G_TYPE_STRING
);
97 g_value_set_string (val
, "none");
98 g_hash_table_insert (config
, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV
, val
);
100 val
= g_slice_new0(GValue
);
101 g_value_init(val
, G_TYPE_UINT
);
102 g_value_set_uint(val
, *(u_int32_t
*)me
->get_address(me
).ptr
);
103 g_hash_table_insert(config
, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS
, val
);
105 val
= g_slice_new0(GValue
);
106 g_value_init(val
, G_TYPE_UINT
);
107 g_value_set_uint(val
, me
->get_address(me
).len
* 8);
108 g_hash_table_insert(config
, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX
, val
);
110 val
= handler_to_val(handler
, INTERNAL_IP4_DNS
);
111 g_hash_table_insert(config
, NM_VPN_PLUGIN_IP4_CONFIG_DNS
, val
);
113 val
= handler_to_val(handler
, INTERNAL_IP4_NBNS
);
114 g_hash_table_insert(config
, NM_VPN_PLUGIN_IP4_CONFIG_NBNS
, val
);
116 handler
->reset(handler
);
118 nm_vpn_plugin_set_ip4_config(plugin
, config
);
122 * signal failure to NM, connecting failed
124 static void signal_failure(NMVPNPlugin
*plugin
, NMVPNPluginFailure failure
)
126 nm_handler_t
*handler
= NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin
)->handler
;
128 handler
->reset(handler
);
130 /* TODO: NM does not handle this failure!? */
131 nm_vpn_plugin_failure(plugin
, failure
);
132 nm_vpn_plugin_set_state(plugin
, NM_VPN_SERVICE_STATE_STOPPED
);
136 * Implementation of listener_t.ike_state_change
138 static bool ike_state_change(listener_t
*listener
, ike_sa_t
*ike_sa
,
139 ike_sa_state_t state
)
141 NMStrongswanPluginPrivate
*private = (NMStrongswanPluginPrivate
*)listener
;
143 if (private->ike_sa
== ike_sa
&& state
== IKE_DESTROYING
)
145 signal_failure(private->plugin
, NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED
);
152 * Implementation of listener_t.child_state_change
154 static bool child_state_change(listener_t
*listener
, ike_sa_t
*ike_sa
,
155 child_sa_t
*child_sa
, child_sa_state_t state
)
157 NMStrongswanPluginPrivate
*private = (NMStrongswanPluginPrivate
*)listener
;
159 if (private->ike_sa
== ike_sa
&& state
== CHILD_DESTROYING
)
161 signal_failure(private->plugin
, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED
);
168 * Implementation of listener_t.child_updown
170 static bool child_updown(listener_t
*listener
, ike_sa_t
*ike_sa
,
171 child_sa_t
*child_sa
, bool up
)
173 NMStrongswanPluginPrivate
*private = (NMStrongswanPluginPrivate
*)listener
;
175 if (private->ike_sa
== ike_sa
)
178 { /* disable initiate-failure-detection hooks */
179 private->listener
.ike_state_change
= NULL
;
180 private->listener
.child_state_change
= NULL
;
181 signal_ipv4_config(private->plugin
, ike_sa
, child_sa
);
185 signal_failure(private->plugin
, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED
);
193 * Implementation of listener_t.ike_rekey
195 static bool ike_rekey(listener_t
*listener
, ike_sa_t
*old
, ike_sa_t
*new)
197 NMStrongswanPluginPrivate
*private = (NMStrongswanPluginPrivate
*)listener
;
199 if (private->ike_sa
== old
)
200 { /* follow a rekeyed IKE_SA */
201 private->ike_sa
= new;
207 * Connect function called from NM via DBUS
209 static gboolean
connect_(NMVPNPlugin
*plugin
, NMConnection
*connection
,
212 NMStrongswanPluginPrivate
*priv
;
213 NMSettingConnection
*conn
;
215 identification_t
*user
= NULL
, *gateway
= NULL
;
216 const char *address
, *str
;
217 bool virtual, encap
, ipcomp
;
219 peer_cfg_t
*peer_cfg
;
220 child_cfg_t
*child_cfg
;
221 traffic_selector_t
*ts
;
224 auth_class_t auth_class
= AUTH_CLASS_EAP
;
225 certificate_t
*cert
= NULL
;
228 lifetime_cfg_t lifetime
= {
230 .life
= 10800 /* 3h */,
231 .rekey
= 10200 /* 2h50min */,
232 .jitter
= 300 /* 5min */
239 priv
= NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin
);
240 conn
= NM_SETTING_CONNECTION(nm_connection_get_setting(connection
,
241 NM_TYPE_SETTING_CONNECTION
));
242 vpn
= NM_SETTING_VPN(nm_connection_get_setting(connection
,
243 NM_TYPE_SETTING_VPN
));
248 priv
->name
= strdup(nm_setting_connection_get_id(conn
));
249 DBG1(DBG_CFG
, "received initiate for NetworkManager connection %s",
252 nm_setting_to_string(NM_SETTING(vpn
)));
253 address
= nm_setting_vpn_get_data_item(vpn
, "address");
254 if (!address
|| !*address
)
256 g_set_error(err
, NM_VPN_PLUGIN_ERROR
, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS
,
257 "Gateway address missing.");
260 str
= nm_setting_vpn_get_data_item(vpn
, "virtual");
261 virtual = str
&& streq(str
, "yes");
262 str
= nm_setting_vpn_get_data_item(vpn
, "encap");
263 encap
= str
&& streq(str
, "yes");
264 str
= nm_setting_vpn_get_data_item(vpn
, "ipcomp");
265 ipcomp
= str
&& streq(str
, "yes");
266 str
= nm_setting_vpn_get_data_item(vpn
, "method");
269 if (streq(str
, "psk"))
271 auth_class
= AUTH_CLASS_PSK
;
273 else if (streq(str
, "agent"))
275 auth_class
= AUTH_CLASS_PUBKEY
;
278 else if (streq(str
, "key"))
280 auth_class
= AUTH_CLASS_PUBKEY
;
285 * Register credentials
287 priv
->creds
->clear(priv
->creds
);
289 /* gateway/CA cert */
290 str
= nm_setting_vpn_get_data_item(vpn
, "certificate");
293 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
294 BUILD_FROM_FILE
, str
, BUILD_END
);
297 g_set_error(err
, NM_VPN_PLUGIN_ERROR
,
298 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS
,
299 "Loading gateway certificate failed.");
302 priv
->creds
->add_certificate(priv
->creds
, cert
);
304 x509
= (x509_t
*)cert
;
305 if (!(x509
->get_flags(x509
) & X509_CA
))
306 { /* For a gateway certificate, we use the cert subject as identity. */
307 gateway
= cert
->get_subject(cert
);
308 gateway
= gateway
->clone(gateway
);
309 DBG1(DBG_CFG
, "using gateway certificate, identity '%Y'", gateway
);
314 /* no certificate defined, fall back to system-wide CA certificates */
315 priv
->creds
->load_ca_dir(priv
->creds
, NM_CA_DIR
);
319 /* If the user configured a CA certificate, we use the IP/DNS
320 * of the gateway as its identity. This identity will be used for
321 * certificate lookup and requires the configured IP/DNS to be
322 * included in the gateway certificate. */
323 gateway
= identification_create_from_string((char*)address
);
324 DBG1(DBG_CFG
, "using CA certificate, gateway identity '%Y'", gateway
);
327 if (auth_class
== AUTH_CLASS_EAP
)
329 /* username/password authentication ... */
330 str
= nm_setting_vpn_get_data_item(vpn
, "user");
333 user
= identification_create_from_string((char*)str
);
334 str
= nm_setting_vpn_get_secret(vpn
, "password");
335 priv
->creds
->set_username_password(priv
->creds
, user
, (char*)str
);
339 if (auth_class
== AUTH_CLASS_PUBKEY
)
341 /* ... or certificate/private key authenitcation */
342 str
= nm_setting_vpn_get_data_item(vpn
, "usercert");
345 public_key_t
*public;
346 private_key_t
*private = NULL
;
348 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
349 BUILD_FROM_FILE
, str
, BUILD_END
);
352 g_set_error(err
, NM_VPN_PLUGIN_ERROR
,
353 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS
,
354 "Loading peer certificate failed.");
355 gateway
->destroy(gateway
);
359 str
= nm_setting_vpn_get_secret(vpn
, "agent");
362 public = cert
->get_public_key(cert
);
365 private = lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
,
366 public->get_type(public),
367 BUILD_AGENT_SOCKET
, str
,
368 BUILD_PUBLIC_KEY
, public,
370 public->destroy(public);
374 g_set_error(err
, NM_VPN_PLUGIN_ERROR
,
375 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS
,
376 "Connecting to SSH agent failed.");
379 /* ... or key file */
380 str
= nm_setting_vpn_get_data_item(vpn
, "userkey");
385 secret
.ptr
= (char*)nm_setting_vpn_get_secret(vpn
, "password");
388 secret
.len
= strlen(secret
.ptr
);
390 private = lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
,
391 KEY_RSA
, BUILD_FROM_FILE
, str
,
392 BUILD_PASSPHRASE
, secret
, BUILD_END
);
395 g_set_error(err
, NM_VPN_PLUGIN_ERROR
,
396 NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS
,
397 "Loading private key failed.");
402 user
= cert
->get_subject(cert
);
403 user
= user
->clone(user
);
404 priv
->creds
->set_cert_and_key(priv
->creds
, cert
, private);
409 gateway
->destroy(gateway
);
417 g_set_error(err
, NM_VPN_PLUGIN_ERROR
, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS
,
418 "Configuration parameters missing.");
419 gateway
->destroy(gateway
);
424 * Set up configurations
426 ike_cfg
= ike_cfg_create(TRUE
, encap
,
427 "0.0.0.0", IKEV2_UDP_PORT
, (char*)address
, IKEV2_UDP_PORT
);
428 ike_cfg
->add_proposal(ike_cfg
, proposal_create_default(PROTO_IKE
));
429 peer_cfg
= peer_cfg_create(priv
->name
, 2, ike_cfg
,
430 CERT_SEND_IF_ASKED
, UNIQUE_REPLACE
, 1, /* keyingtries */
431 36000, 0, /* rekey 10h, reauth none */
432 600, 600, /* jitter, over 10min */
433 TRUE
, 0, /* mobike, DPD */
434 virtual ? host_create_from_string("0.0.0.0", 0) : NULL
,
435 NULL
, FALSE
, NULL
, NULL
); /* pool, mediation */
436 auth
= auth_cfg_create();
437 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, auth_class
);
438 auth
->add(auth
, AUTH_RULE_IDENTITY
, user
);
439 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, TRUE
);
440 auth
= auth_cfg_create();
441 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_PUBKEY
);
442 auth
->add(auth
, AUTH_RULE_IDENTITY
, gateway
);
443 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, FALSE
);
445 child_cfg
= child_cfg_create(priv
->name
, &lifetime
,
446 NULL
, TRUE
, MODE_TUNNEL
, /* updown, hostaccess */
447 ACTION_NONE
, ACTION_NONE
, ipcomp
, 0, 0, NULL
);
448 child_cfg
->add_proposal(child_cfg
, proposal_create_default(PROTO_ESP
));
449 ts
= traffic_selector_create_dynamic(0, 0, 65535);
450 child_cfg
->add_traffic_selector(child_cfg
, TRUE
, ts
);
451 ts
= traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE
,
453 "255.255.255.255", 65535);
454 child_cfg
->add_traffic_selector(child_cfg
, FALSE
, ts
);
455 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
);
460 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
462 if (!ike_sa
->get_peer_cfg(ike_sa
))
464 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
466 peer_cfg
->destroy(peer_cfg
);
469 * Register listener, enable initiate-failure-detection hooks
471 priv
->ike_sa
= ike_sa
;
472 priv
->listener
.ike_state_change
= ike_state_change
;
473 priv
->listener
.child_state_change
= child_state_change
;
474 charon
->bus
->add_listener(charon
->bus
, &priv
->listener
);
479 if (ike_sa
->initiate(ike_sa
, child_cfg
, 0, NULL
, NULL
) != SUCCESS
)
481 charon
->bus
->remove_listener(charon
->bus
, &priv
->listener
);
482 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
, ike_sa
);
484 g_set_error(err
, NM_VPN_PLUGIN_ERROR
, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED
,
485 "Initiating failed.");
488 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
493 * NeedSecrets called from NM via DBUS
495 static gboolean
need_secrets(NMVPNPlugin
*plugin
, NMConnection
*connection
,
496 char **setting_name
, GError
**error
)
498 NMSettingVPN
*settings
;
499 const char *method
, *path
;
501 settings
= NM_SETTING_VPN(nm_connection_get_setting(connection
,
502 NM_TYPE_SETTING_VPN
));
503 method
= nm_setting_vpn_get_data_item(settings
, "method");
506 if (streq(method
, "eap"))
508 if (nm_setting_vpn_get_secret(settings
, "password"))
513 else if (streq(method
, "agent"))
515 if (nm_setting_vpn_get_secret(settings
, "agent"))
520 else if (streq(method
, "key"))
522 path
= nm_setting_vpn_get_data_item(settings
, "userkey");
528 secret
.ptr
= (char*)nm_setting_vpn_get_secret(settings
, "password");
531 secret
.len
= strlen(secret
.ptr
);
533 /* try to load/decrypt the private key */
534 key
= lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
,
535 KEY_RSA
, BUILD_FROM_FILE
, path
,
536 BUILD_PASSPHRASE
, secret
, BUILD_END
);
545 *setting_name
= NM_SETTING_VPN_SETTING_NAME
;
550 * Disconnect called from NM via DBUS
552 static gboolean
disconnect(NMVPNPlugin
*plugin
, GError
**err
)
554 NMStrongswanPluginPrivate
*priv
= NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin
);
555 enumerator_t
*enumerator
;
559 /* our ike_sa pointer might be invalid, lookup sa */
560 enumerator
= charon
->controller
->create_ike_sa_enumerator(charon
->controller
);
561 while (enumerator
->enumerate(enumerator
, &ike_sa
))
563 if (priv
->ike_sa
== ike_sa
)
565 id
= ike_sa
->get_unique_id(ike_sa
);
566 enumerator
->destroy(enumerator
);
567 charon
->controller
->terminate_ike(charon
->controller
, id
,
568 controller_cb_empty
, NULL
);
572 enumerator
->destroy(enumerator
);
574 g_set_error(err
, NM_VPN_PLUGIN_ERROR
, NM_VPN_PLUGIN_ERROR_GENERAL
,
575 "Connection not found.");
582 static void nm_strongswan_plugin_init(NMStrongswanPlugin
*plugin
)
584 NMStrongswanPluginPrivate
*priv
;
586 priv
= NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin
);
587 priv
->plugin
= NM_VPN_PLUGIN(plugin
);
588 memset(&priv
->listener
.log
, 0, sizeof(listener_t
));
589 priv
->listener
.child_updown
= child_updown
;
590 priv
->listener
.ike_rekey
= ike_rekey
;
596 static void nm_strongswan_plugin_class_init(
597 NMStrongswanPluginClass
*strongswan_class
)
599 NMVPNPluginClass
*parent_class
= NM_VPN_PLUGIN_CLASS(strongswan_class
);
601 g_type_class_add_private(G_OBJECT_CLASS(strongswan_class
),
602 sizeof(NMStrongswanPluginPrivate
));
603 parent_class
->connect
= connect_
;
604 parent_class
->need_secrets
= need_secrets
;
605 parent_class
->disconnect
= disconnect
;
611 NMStrongswanPlugin
*nm_strongswan_plugin_new(nm_creds_t
*creds
,
612 nm_handler_t
*handler
)
614 NMStrongswanPlugin
*plugin
= (NMStrongswanPlugin
*)g_object_new (
615 NM_TYPE_STRONGSWAN_PLUGIN
,
616 NM_VPN_PLUGIN_DBUS_SERVICE_NAME
, NM_DBUS_SERVICE_STRONGSWAN
,
620 NMStrongswanPluginPrivate
*priv
;
622 priv
= NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin
);
624 priv
->handler
= handler
;