]>
Commit | Line | Data |
---|---|---|
6dbce9c8 | 1 | /* |
9a71b721 LR |
2 | * Copyright (C) 2017 Lubomir Rintel |
3 | * | |
bc3eda99 | 4 | * Copyright (C) 2013-2020 Tobias Brunner |
3ab0e8a0 | 5 | * Copyright (C) 2008-2009 Martin Willi |
1b671669 | 6 | * HSR Hochschule fuer Technik Rapperswil |
6dbce9c8 MW |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | * for more details. | |
6dbce9c8 MW |
17 | */ |
18 | ||
6dbce9c8 MW |
19 | #include "nm_service.h" |
20 | ||
21 | #include <daemon.h> | |
2e7cc07e | 22 | #include <networking/host.h> |
6dbce9c8 MW |
23 | #include <utils/identification.h> |
24 | #include <config/peer_cfg.h> | |
0ed6b7a7 | 25 | #include <credentials/certificates/x509.h> |
aa3d5bf7 | 26 | #include <networking/tun_device.h> |
6dbce9c8 MW |
27 | |
28 | #include <stdio.h> | |
29 | ||
6dbce9c8 MW |
30 | /** |
31 | * Private data of NMStrongswanPlugin | |
32 | */ | |
33 | typedef struct { | |
3ab0e8a0 | 34 | /* implements bus listener interface */ |
a985db3f | 35 | listener_t listener; |
3ab0e8a0 | 36 | /* IKE_SA we are listening on */ |
6dbce9c8 | 37 | ike_sa_t *ike_sa; |
3ab0e8a0 | 38 | /* backref to public plugin */ |
9a71b721 | 39 | NMVpnServicePlugin *plugin; |
3ab0e8a0 | 40 | /* credentials to use for authentication */ |
ec249871 | 41 | nm_creds_t *creds; |
3ab0e8a0 MW |
42 | /* attribute handler for DNS/NBNS server information */ |
43 | nm_handler_t *handler; | |
aa3d5bf7 TB |
44 | /* dummy TUN device */ |
45 | tun_device_t *tun; | |
69b80589 MW |
46 | /* name of the connection */ |
47 | char *name; | |
6dbce9c8 MW |
48 | } NMStrongswanPluginPrivate; |
49 | ||
18bee930 TB |
50 | G_DEFINE_TYPE_WITH_PRIVATE(NMStrongswanPlugin, nm_strongswan_plugin, NM_TYPE_VPN_SERVICE_PLUGIN) |
51 | ||
6dbce9c8 | 52 | #define NM_STRONGSWAN_PLUGIN_GET_PRIVATE(o) \ |
18bee930 TB |
53 | ((NMStrongswanPluginPrivate*) \ |
54 | nm_strongswan_plugin_get_instance_private (o)) | |
6dbce9c8 | 55 | |
3ab0e8a0 | 56 | /** |
05b7f1cb | 57 | * Convert an address chunk to a GValue |
3ab0e8a0 | 58 | */ |
05b7f1cb TB |
59 | static GVariant *addr_to_variant(chunk_t addr) |
60 | { | |
61 | GVariantBuilder builder; | |
62 | int i; | |
63 | ||
64 | switch (addr.len) | |
65 | { | |
66 | case 4: | |
67 | return g_variant_new_uint32 (*(uint32_t*)addr.ptr); | |
68 | case 16: | |
69 | g_variant_builder_init (&builder, G_VARIANT_TYPE ("ay")); | |
70 | for (i = 0; i < addr.len; i++) | |
71 | { | |
72 | g_variant_builder_add (&builder, "y", addr.ptr[i]); | |
73 | ||
74 | } | |
75 | return g_variant_builder_end (&builder); | |
76 | default: | |
77 | return NULL; | |
78 | } | |
79 | } | |
80 | ||
81 | /** | |
82 | * Convert a host to a GValue | |
83 | */ | |
84 | static GVariant *host_to_variant(host_t *host) | |
85 | { | |
86 | return addr_to_variant(host->get_address(host)); | |
87 | } | |
88 | ||
89 | /** | |
90 | * Convert enumerated handler chunks to a GValue | |
91 | */ | |
92 | static GVariant* handler_to_variant(nm_handler_t *handler, char *variant_type, | |
3ab0e8a0 MW |
93 | configuration_attribute_type_t type) |
94 | { | |
9a71b721 | 95 | GVariantBuilder builder; |
3ab0e8a0 | 96 | enumerator_t *enumerator; |
0af3a4f1 | 97 | chunk_t *chunk; |
7daf5226 | 98 | |
05b7f1cb | 99 | g_variant_builder_init (&builder, G_VARIANT_TYPE (variant_type)); |
9a71b721 | 100 | |
3ab0e8a0 | 101 | enumerator = handler->create_enumerator(handler, type); |
3ab0e8a0 MW |
102 | while (enumerator->enumerate(enumerator, &chunk)) |
103 | { | |
05b7f1cb | 104 | g_variant_builder_add_value (&builder, addr_to_variant(*chunk)); |
3ab0e8a0 MW |
105 | } |
106 | enumerator->destroy(enumerator); | |
7daf5226 | 107 | |
9a71b721 | 108 | return g_variant_builder_end (&builder); |
3ab0e8a0 MW |
109 | } |
110 | ||
6dbce9c8 | 111 | /** |
05b7f1cb | 112 | * Signal IP config to NM, set connection as established |
6dbce9c8 | 113 | */ |
05b7f1cb TB |
114 | static void signal_ip_config(NMVpnServicePlugin *plugin, |
115 | ike_sa_t *ike_sa, child_sa_t *child_sa) | |
6dbce9c8 | 116 | { |
18bee930 TB |
117 | NMStrongswanPlugin *pub = (NMStrongswanPlugin*)plugin; |
118 | NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(pub); | |
05b7f1cb TB |
119 | GVariantBuilder builder, ip4builder, ip6builder; |
120 | GVariant *ip4config, *ip6config; | |
9cf09eca | 121 | enumerator_t *enumerator; |
05b7f1cb | 122 | host_t *me, *other, *vip4 = NULL, *vip6 = NULL; |
3ab0e8a0 | 123 | nm_handler_t *handler; |
7daf5226 | 124 | |
9a71b721 | 125 | g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); |
05b7f1cb TB |
126 | g_variant_builder_init (&ip4builder, G_VARIANT_TYPE_VARDICT); |
127 | g_variant_builder_init (&ip6builder, G_VARIANT_TYPE_VARDICT); | |
9a71b721 | 128 | |
c15eea73 | 129 | handler = priv->handler; |
7daf5226 | 130 | |
f201d86d | 131 | /* NM apparently requires to know the gateway */ |
f201d86d | 132 | other = ike_sa->get_other_host(ike_sa); |
05b7f1cb | 133 | g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, |
aa3d5bf7 TB |
134 | host_to_variant(other)); |
135 | ||
136 | /* systemd-resolved requires a device to properly install DNS servers, but | |
137 | * Netkey does not use one. Passing the physical interface is not ideal, | |
138 | * as NM fiddles around with it and systemd-resolved likes a separate | |
139 | * device. So we pass a dummy TUN device along for NM etc. to play with... | |
140 | */ | |
141 | if (priv->tun) | |
142 | { | |
143 | g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV, | |
144 | g_variant_new_string (priv->tun->get_name(priv->tun))); | |
145 | } | |
f201d86d | 146 | |
05b7f1cb | 147 | /* pass the first virtual IPs we got or use the physical IP */ |
9cf09eca | 148 | enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); |
05b7f1cb | 149 | while (enumerator->enumerate(enumerator, &me)) |
9cf09eca | 150 | { |
05b7f1cb TB |
151 | switch (me->get_family(me)) |
152 | { | |
153 | case AF_INET: | |
154 | if (!vip4) | |
155 | { | |
156 | vip4 = me; | |
157 | } | |
158 | break; | |
159 | case AF_INET6: | |
160 | if (!vip6) | |
161 | { | |
162 | vip6 = me; | |
163 | } | |
164 | break; | |
165 | } | |
9cf09eca TB |
166 | } |
167 | enumerator->destroy(enumerator); | |
05b7f1cb TB |
168 | if (!vip4 && !vip6) |
169 | { | |
170 | me = ike_sa->get_my_host(ike_sa); | |
171 | switch (me->get_family(me)) | |
172 | { | |
173 | case AF_INET: | |
174 | vip4 = me; | |
175 | break; | |
176 | case AF_INET6: | |
177 | vip6 = me; | |
178 | break; | |
179 | } | |
180 | } | |
7daf5226 | 181 | |
05b7f1cb TB |
182 | if (vip4) |
183 | { | |
184 | g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, | |
185 | host_to_variant(vip4)); | |
186 | g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, | |
187 | g_variant_new_uint32 (vip4->get_address(vip4).len * 8)); | |
188 | ||
189 | /* prevent NM from changing the default route. we set our own route in our | |
190 | * own routing table | |
191 | */ | |
192 | g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, | |
193 | g_variant_new_boolean (TRUE)); | |
194 | ||
195 | g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, | |
196 | handler_to_variant(handler, "au", INTERNAL_IP4_DNS)); | |
197 | ||
198 | g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS, | |
199 | handler_to_variant(handler, "au", INTERNAL_IP4_NBNS)); | |
200 | } | |
3651c8dc | 201 | |
05b7f1cb TB |
202 | if (vip6) |
203 | { | |
204 | g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, | |
205 | host_to_variant(vip6)); | |
206 | g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, | |
207 | g_variant_new_uint32 (vip6->get_address(vip6).len * 8)); | |
208 | g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT, | |
209 | g_variant_new_boolean (TRUE)); | |
210 | g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DNS, | |
211 | handler_to_variant(handler, "aay", INTERNAL_IP6_DNS)); | |
212 | /* NM_VPN_PLUGIN_IP6_CONFIG_NBNS is not defined */ | |
213 | } | |
7daf5226 | 214 | |
05b7f1cb TB |
215 | ip4config = g_variant_builder_end (&ip4builder); |
216 | if (g_variant_n_children (ip4config)) | |
217 | { | |
218 | g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP4, | |
219 | g_variant_new_boolean (TRUE)); | |
220 | } | |
221 | else | |
222 | { | |
223 | g_variant_unref (ip4config); | |
224 | ip4config = NULL; | |
225 | } | |
9a71b721 | 226 | |
05b7f1cb TB |
227 | ip6config = g_variant_builder_end (&ip6builder); |
228 | if (g_variant_n_children (ip6config)) | |
229 | { | |
230 | g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP6, | |
231 | g_variant_new_boolean (TRUE)); | |
232 | } | |
233 | else | |
234 | { | |
235 | g_variant_unref (ip6config); | |
236 | ip6config = NULL; | |
237 | } | |
7daf5226 | 238 | |
3ab0e8a0 | 239 | handler->reset(handler); |
7daf5226 | 240 | |
05b7f1cb TB |
241 | nm_vpn_service_plugin_set_config (plugin, g_variant_builder_end (&builder)); |
242 | if (ip4config) | |
243 | { | |
244 | nm_vpn_service_plugin_set_ip4_config (plugin, ip4config); | |
245 | } | |
246 | if (ip6config) | |
247 | { | |
248 | nm_vpn_service_plugin_set_ip6_config (plugin, ip6config); | |
249 | } | |
6dbce9c8 MW |
250 | } |
251 | ||
252 | /** | |
a985db3f | 253 | * signal failure to NM, connecting failed |
6dbce9c8 | 254 | */ |
9a71b721 | 255 | static void signal_failure(NMVpnServicePlugin *plugin, NMVpnPluginFailure failure) |
a985db3f | 256 | { |
18bee930 TB |
257 | NMStrongswanPlugin *pub = (NMStrongswanPlugin*)plugin; |
258 | nm_handler_t *handler = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(pub)->handler; | |
7daf5226 | 259 | |
3ab0e8a0 | 260 | handler->reset(handler); |
7daf5226 | 261 | |
9a71b721 | 262 | nm_vpn_service_plugin_failure(plugin, failure); |
a985db3f MW |
263 | } |
264 | ||
3d2f5ae0 TB |
265 | METHOD(listener_t, ike_state_change, bool, |
266 | NMStrongswanPluginPrivate *this, ike_sa_t *ike_sa, ike_sa_state_t state) | |
6dbce9c8 | 267 | { |
3d2f5ae0 | 268 | if (this->ike_sa == ike_sa && state == IKE_DESTROYING) |
6dbce9c8 | 269 | { |
3d2f5ae0 | 270 | signal_failure(this->plugin, NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED); |
a985db3f MW |
271 | } |
272 | return TRUE; | |
273 | } | |
274 | ||
3d2f5ae0 TB |
275 | METHOD(listener_t, child_state_change, bool, |
276 | NMStrongswanPluginPrivate *this, ike_sa_t *ike_sa, child_sa_t *child_sa, | |
277 | child_sa_state_t state) | |
a985db3f | 278 | { |
3d2f5ae0 | 279 | if (this->ike_sa == ike_sa && state == CHILD_DESTROYING) |
bad99d5a | 280 | { |
3d2f5ae0 | 281 | signal_failure(this->plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED); |
bad99d5a MW |
282 | } |
283 | return TRUE; | |
284 | } | |
a985db3f | 285 | |
3d2f5ae0 TB |
286 | METHOD(listener_t, ike_rekey, bool, |
287 | NMStrongswanPluginPrivate *this, ike_sa_t *old, ike_sa_t *new) | |
bad99d5a | 288 | { |
3d2f5ae0 TB |
289 | if (this->ike_sa == old) |
290 | { /* follow a rekeyed IKE_SA */ | |
291 | this->ike_sa = new; | |
292 | } | |
293 | return TRUE; | |
294 | } | |
295 | ||
296 | METHOD(listener_t, ike_reestablish_pre, bool, | |
297 | NMStrongswanPluginPrivate *this, ike_sa_t *old, ike_sa_t *new) | |
298 | { | |
299 | if (this->ike_sa == old) | |
300 | { /* ignore child state changes during redirects etc. (task migration) */ | |
301 | this->listener.child_state_change = NULL; | |
302 | } | |
303 | return TRUE; | |
304 | } | |
7daf5226 | 305 | |
3d2f5ae0 TB |
306 | METHOD(listener_t, ike_reestablish_post, bool, |
307 | NMStrongswanPluginPrivate *this, ike_sa_t *old, ike_sa_t *new, | |
308 | bool initiated) | |
309 | { | |
310 | if (this->ike_sa == old && initiated) | |
311 | { /* if we get redirected during IKE_AUTH we just migrate to the new SA */ | |
312 | this->ike_sa = new; | |
313 | /* re-register hooks to detect initiation failures */ | |
314 | this->listener.ike_state_change = _ike_state_change; | |
315 | this->listener.child_state_change = _child_state_change; | |
316 | } | |
317 | return TRUE; | |
318 | } | |
319 | ||
320 | METHOD(listener_t, child_updown, bool, | |
321 | NMStrongswanPluginPrivate *this, ike_sa_t *ike_sa, child_sa_t *child_sa, | |
322 | bool up) | |
323 | { | |
10a91368 | 324 | if (this->ike_sa == ike_sa && up) |
a985db3f | 325 | { |
10a91368 TB |
326 | /* disable initiate-failure-detection hooks */ |
327 | this->listener.ike_state_change = NULL; | |
328 | this->listener.child_state_change = NULL; | |
329 | signal_ip_config(this->plugin, ike_sa, child_sa); | |
6dbce9c8 MW |
330 | } |
331 | return TRUE; | |
332 | } | |
333 | ||
8bec0f51 MW |
334 | /** |
335 | * Find a certificate for which we have a private key on a smartcard | |
336 | */ | |
337 | static identification_t *find_smartcard_key(NMStrongswanPluginPrivate *priv, | |
338 | char *pin) | |
339 | { | |
340 | enumerator_t *enumerator, *sans; | |
341 | identification_t *id = NULL; | |
342 | certificate_t *cert; | |
343 | x509_t *x509; | |
344 | private_key_t *key; | |
345 | chunk_t keyid; | |
346 | ||
347 | enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, | |
348 | CERT_X509, KEY_ANY, NULL, FALSE); | |
349 | while (enumerator->enumerate(enumerator, &cert)) | |
350 | { | |
351 | x509 = (x509_t*)cert; | |
352 | ||
353 | /* there might be a lot of certificates, filter them by usage */ | |
354 | if ((x509->get_flags(x509) & X509_CLIENT_AUTH) && | |
355 | !(x509->get_flags(x509) & X509_CA)) | |
356 | { | |
357 | keyid = x509->get_subjectKeyIdentifier(x509); | |
358 | if (keyid.ptr) | |
359 | { | |
360 | /* try to find a private key by the certificate keyid */ | |
361 | priv->creds->set_pin(priv->creds, keyid, pin); | |
362 | key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, | |
363 | KEY_ANY, BUILD_PKCS11_KEYID, keyid, BUILD_END); | |
364 | if (key) | |
365 | { | |
366 | /* prefer a more convenient subjectAltName */ | |
367 | sans = x509->create_subjectAltName_enumerator(x509); | |
368 | if (!sans->enumerate(sans, &id)) | |
369 | { | |
370 | id = cert->get_subject(cert); | |
371 | } | |
372 | id = id->clone(id); | |
373 | sans->destroy(sans); | |
374 | ||
375 | DBG1(DBG_CFG, "using smartcard certificate '%Y'", id); | |
376 | priv->creds->set_cert_and_key(priv->creds, | |
377 | cert->get_ref(cert), key); | |
378 | break; | |
379 | } | |
380 | } | |
381 | } | |
382 | } | |
383 | enumerator->destroy(enumerator); | |
384 | return id; | |
385 | } | |
386 | ||
bc3eda99 TB |
387 | /** |
388 | * Add a client auth config for certificate authentication | |
389 | */ | |
390 | static bool add_auth_cfg_cert(NMStrongswanPluginPrivate *priv, | |
391 | NMSettingVpn *vpn, peer_cfg_t *peer_cfg, | |
392 | GError **err) | |
393 | { | |
ff8f6b15 | 394 | identification_t *id = NULL; |
bc3eda99 TB |
395 | certificate_t *cert = NULL; |
396 | auth_cfg_t *auth; | |
397 | const char *str, *method, *cert_source; | |
398 | ||
399 | method = nm_setting_vpn_get_data_item(vpn, "method"); | |
400 | cert_source = nm_setting_vpn_get_data_item(vpn, "cert-source") ?: method; | |
401 | ||
402 | if (streq(cert_source, "smartcard")) | |
403 | { | |
404 | char *pin; | |
405 | ||
406 | pin = (char*)nm_setting_vpn_get_secret(vpn, "password"); | |
407 | if (pin) | |
408 | { | |
ff8f6b15 | 409 | id = find_smartcard_key(priv, pin); |
bc3eda99 | 410 | } |
ff8f6b15 | 411 | if (!id) |
bc3eda99 TB |
412 | { |
413 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
414 | NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
ff8f6b15 | 415 | "No usable smartcard certificate found."); |
bc3eda99 TB |
416 | return FALSE; |
417 | } | |
418 | } | |
419 | /* ... or certificate/private key authentication */ | |
420 | else if ((str = nm_setting_vpn_get_data_item(vpn, "usercert"))) | |
421 | { | |
422 | public_key_t *public; | |
423 | private_key_t *private = NULL; | |
424 | ||
425 | bool agent = streq(cert_source, "agent"); | |
426 | ||
427 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, | |
428 | BUILD_FROM_FILE, str, BUILD_END); | |
429 | if (!cert) | |
430 | { | |
431 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
432 | NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
433 | "Loading peer certificate failed."); | |
434 | return FALSE; | |
435 | } | |
436 | /* try agent */ | |
437 | str = nm_setting_vpn_get_secret(vpn, "agent"); | |
438 | if (agent && str) | |
439 | { | |
440 | public = cert->get_public_key(cert); | |
441 | if (public) | |
442 | { | |
443 | private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, | |
444 | public->get_type(public), | |
445 | BUILD_AGENT_SOCKET, str, | |
446 | BUILD_PUBLIC_KEY, public, | |
447 | BUILD_END); | |
448 | public->destroy(public); | |
449 | } | |
450 | if (!private) | |
451 | { | |
452 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
453 | NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
454 | "Connecting to SSH agent failed."); | |
455 | } | |
456 | } | |
457 | /* ... or key file */ | |
458 | str = nm_setting_vpn_get_data_item(vpn, "userkey"); | |
459 | if (!agent && str) | |
460 | { | |
461 | char *secret; | |
462 | ||
463 | secret = (char*)nm_setting_vpn_get_secret(vpn, "password"); | |
464 | if (secret) | |
465 | { | |
466 | priv->creds->set_key_password(priv->creds, secret); | |
467 | } | |
468 | private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, | |
469 | KEY_ANY, BUILD_FROM_FILE, str, BUILD_END); | |
470 | if (!private) | |
471 | { | |
472 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
473 | NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
474 | "Loading private key failed."); | |
475 | } | |
476 | } | |
477 | if (private) | |
478 | { | |
ff8f6b15 TB |
479 | id = cert->get_subject(cert); |
480 | id = id->clone(id); | |
bc3eda99 TB |
481 | priv->creds->set_cert_and_key(priv->creds, cert, private); |
482 | } | |
483 | else | |
484 | { | |
485 | DESTROY_IF(cert); | |
486 | return FALSE; | |
487 | } | |
488 | } | |
ff8f6b15 TB |
489 | else |
490 | { | |
491 | g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
492 | "Certificate is missing."); | |
493 | return FALSE; | |
494 | } | |
bc3eda99 TB |
495 | |
496 | auth = auth_cfg_create(); | |
497 | if (streq(method, "eap-tls")) | |
498 | { | |
499 | auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); | |
500 | auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS); | |
501 | auth->add(auth, AUTH_RULE_AAA_IDENTITY, | |
502 | identification_create_from_string("%any")); | |
503 | } | |
504 | else | |
505 | { | |
506 | auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); | |
507 | } | |
508 | if (cert) | |
509 | { | |
510 | auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert)); | |
511 | } | |
ff8f6b15 TB |
512 | str = nm_setting_vpn_get_data_item(vpn, "local-identity"); |
513 | if (str) | |
514 | { | |
515 | identification_t *local_id; | |
516 | ||
517 | local_id = identification_create_from_string((char*)str); | |
518 | if (local_id) | |
519 | { | |
520 | id->destroy(id); | |
521 | id = local_id; | |
522 | } | |
523 | } | |
524 | auth->add(auth, AUTH_RULE_IDENTITY, id); | |
bc3eda99 TB |
525 | peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE); |
526 | return TRUE; | |
527 | } | |
528 | ||
529 | /** | |
530 | * Add a client auth config for username/password authentication | |
531 | */ | |
532 | static bool add_auth_cfg_pw(NMStrongswanPluginPrivate *priv, | |
533 | NMSettingVpn *vpn, peer_cfg_t *peer_cfg, | |
534 | GError **err) | |
535 | { | |
ff8f6b15 | 536 | identification_t *user = NULL, *id = NULL; |
bc3eda99 TB |
537 | auth_cfg_t *auth; |
538 | const char *str, *method; | |
539 | ||
540 | method = nm_setting_vpn_get_data_item(vpn, "method"); | |
541 | ||
542 | str = nm_setting_vpn_get_data_item(vpn, "user"); | |
543 | if (str) | |
544 | { | |
545 | user = identification_create_from_string((char*)str); | |
ff8f6b15 TB |
546 | } |
547 | else | |
548 | { | |
549 | user = identification_create_from_string("%any"); | |
550 | } | |
551 | str = nm_setting_vpn_get_data_item(vpn, "local-identity"); | |
552 | if (str) | |
553 | { | |
554 | id = identification_create_from_string((char*)str); | |
555 | } | |
556 | else | |
557 | { | |
558 | id = user->clone(user); | |
559 | } | |
560 | str = nm_setting_vpn_get_secret(vpn, "password"); | |
561 | if (streq(method, "psk")) | |
562 | { | |
563 | if (strlen(str) < 20) | |
bc3eda99 TB |
564 | { |
565 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
566 | NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
567 | "Pre-shared key is too short."); | |
568 | user->destroy(user); | |
ff8f6b15 | 569 | id->destroy(id); |
bc3eda99 TB |
570 | return FALSE; |
571 | } | |
ff8f6b15 | 572 | priv->creds->set_username_password(priv->creds, id, (char*)str); |
bc3eda99 TB |
573 | } |
574 | else | |
575 | { | |
ff8f6b15 | 576 | priv->creds->set_username_password(priv->creds, user, (char*)str); |
bc3eda99 TB |
577 | } |
578 | ||
579 | auth = auth_cfg_create(); | |
580 | auth->add(auth, AUTH_RULE_AUTH_CLASS, | |
581 | streq(method, "psk") ? AUTH_CLASS_PSK : AUTH_CLASS_EAP); | |
582 | /* in case EAP-PEAP or EAP-TTLS is used we currently accept any identity */ | |
583 | auth->add(auth, AUTH_RULE_AAA_IDENTITY, | |
584 | identification_create_from_string("%any")); | |
ff8f6b15 TB |
585 | auth->add(auth, AUTH_RULE_EAP_IDENTITY, user); |
586 | auth->add(auth, AUTH_RULE_IDENTITY, id); | |
bc3eda99 TB |
587 | peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE); |
588 | return TRUE; | |
589 | } | |
590 | ||
6dbce9c8 MW |
591 | /** |
592 | * Connect function called from NM via DBUS | |
593 | */ | |
9a71b721 | 594 | static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection, |
6dbce9c8 MW |
595 | GError **err) |
596 | { | |
18bee930 | 597 | NMStrongswanPlugin *pub = (NMStrongswanPlugin*)plugin; |
69b80589 MW |
598 | NMStrongswanPluginPrivate *priv; |
599 | NMSettingConnection *conn; | |
9a71b721 | 600 | NMSettingVpn *vpn; |
4ac68f02 | 601 | enumerator_t *enumerator; |
bc3eda99 TB |
602 | identification_t *gateway = NULL; |
603 | const char *str, *method; | |
9486a2e5 | 604 | bool virtual, proposal; |
4ac68f02 | 605 | proposal_t *prop; |
6dbce9c8 MW |
606 | ike_cfg_t *ike_cfg; |
607 | peer_cfg_t *peer_cfg; | |
608 | child_cfg_t *child_cfg; | |
609 | traffic_selector_t *ts; | |
610 | ike_sa_t *ike_sa; | |
a44bb934 | 611 | auth_cfg_t *auth; |
a88aae3d | 612 | certificate_t *cert = NULL; |
0ed6b7a7 | 613 | x509_t *x509; |
bc3eda99 | 614 | bool loose_gateway_id = FALSE; |
9486a2e5 TB |
615 | ike_cfg_create_t ike = { |
616 | .version = IKEV2, | |
617 | .local = "%any", | |
618 | .local_port = charon->socket->get_port(charon->socket, FALSE), | |
619 | .remote_port = IKEV2_UDP_PORT, | |
620 | .fragmentation = FRAGMENTATION_YES, | |
621 | }; | |
2ba5dadb TB |
622 | peer_cfg_create_t peer = { |
623 | .cert_policy = CERT_SEND_IF_ASKED, | |
624 | .unique = UNIQUE_REPLACE, | |
2ba5dadb TB |
625 | .rekey_time = 36000, /* 10h */ |
626 | .jitter_time = 600, /* 10min */ | |
627 | .over_time = 600, /* 10min */ | |
628 | }; | |
8a00a845 TB |
629 | child_cfg_create_t child = { |
630 | .lifetime = { | |
631 | .time = { | |
632 | .life = 10800 /* 3h */, | |
633 | .rekey = 10200 /* 2h50min */, | |
634 | .jitter = 300 /* 5min */ | |
635 | }, | |
636 | }, | |
637 | .mode = MODE_TUNNEL, | |
4f4d4021 TB |
638 | .dpd_action = ACTION_START, |
639 | .close_action = ACTION_START, | |
e75f4237 | 640 | }; |
7daf5226 | 641 | |
6dbce9c8 MW |
642 | /** |
643 | * Read parameters | |
644 | */ | |
18bee930 | 645 | priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(pub); |
69b80589 MW |
646 | conn = NM_SETTING_CONNECTION(nm_connection_get_setting(connection, |
647 | NM_TYPE_SETTING_CONNECTION)); | |
648 | vpn = NM_SETTING_VPN(nm_connection_get_setting(connection, | |
649 | NM_TYPE_SETTING_VPN)); | |
650 | if (priv->name) | |
651 | { | |
652 | free(priv->name); | |
653 | } | |
654 | priv->name = strdup(nm_setting_connection_get_id(conn)); | |
655 | DBG1(DBG_CFG, "received initiate for NetworkManager connection %s", | |
656 | priv->name); | |
657 | DBG4(DBG_CFG, "%s", | |
658 | nm_setting_to_string(NM_SETTING(vpn))); | |
aa3d5bf7 TB |
659 | if (!priv->tun) |
660 | { | |
661 | DBG1(DBG_CFG, "failed to create dummy TUN device, might affect DNS " | |
662 | "server installation negatively"); | |
663 | } | |
9486a2e5 TB |
664 | ike.remote = (char*)nm_setting_vpn_get_data_item(vpn, "address"); |
665 | if (!ike.remote || !*ike.remote) | |
6dbce9c8 MW |
666 | { |
667 | g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
323f9f99 | 668 | "Gateway address missing."); |
6dbce9c8 MW |
669 | return FALSE; |
670 | } | |
60777574 TB |
671 | str = nm_setting_vpn_get_data_item(vpn, "server-port"); |
672 | if (str && strlen(str)) | |
673 | { | |
674 | ike.remote_port = settings_value_as_int((char*)str, ike.remote_port); | |
675 | } | |
69b80589 | 676 | str = nm_setting_vpn_get_data_item(vpn, "virtual"); |
8a00a845 | 677 | virtual = streq(str, "yes"); |
69b80589 | 678 | str = nm_setting_vpn_get_data_item(vpn, "encap"); |
9486a2e5 | 679 | ike.force_encap = streq(str, "yes"); |
69b80589 | 680 | str = nm_setting_vpn_get_data_item(vpn, "ipcomp"); |
749ac175 | 681 | child.options |= streq(str, "yes") ? OPT_IPCOMP : 0; |
7daf5226 | 682 | |
ec249871 MW |
683 | /** |
684 | * Register credentials | |
685 | */ | |
69b80589 | 686 | priv->creds->clear(priv->creds); |
7daf5226 | 687 | |
0ed6b7a7 | 688 | /* gateway/CA cert */ |
69b80589 | 689 | str = nm_setting_vpn_get_data_item(vpn, "certificate"); |
ec249871 MW |
690 | if (str) |
691 | { | |
ec249871 MW |
692 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, |
693 | BUILD_FROM_FILE, str, BUILD_END); | |
85af7a89 MW |
694 | if (!cert) |
695 | { | |
696 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
697 | NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
698 | "Loading gateway certificate failed."); | |
699 | return FALSE; | |
700 | } | |
701 | priv->creds->add_certificate(priv->creds, cert); | |
ec249871 | 702 | } |
85af7a89 | 703 | else |
a88aae3d | 704 | { |
85af7a89 | 705 | /* no certificate defined, fall back to system-wide CA certificates */ |
97c74b56 TB |
706 | priv->creds->load_ca_dir(priv->creds, lib->settings->get_str( |
707 | lib->settings, "charon-nm.ca_dir", NM_CA_DIR)); | |
a88aae3d | 708 | } |
19e64e10 TB |
709 | |
710 | str = nm_setting_vpn_get_data_item(vpn, "remote-identity"); | |
711 | if (str) | |
712 | { | |
713 | gateway = identification_create_from_string((char*)str); | |
714 | } | |
715 | else if (cert) | |
716 | { | |
717 | x509 = (x509_t*)cert; | |
718 | if (!(x509->get_flags(x509) & X509_CA)) | |
719 | { /* for server certificates, we use the subject as identity */ | |
720 | gateway = cert->get_subject(cert); | |
721 | gateway = gateway->clone(gateway); | |
722 | } | |
723 | } | |
724 | if (!gateway || gateway->get_type(gateway) == ID_ANY) | |
85af7a89 | 725 | { |
19e64e10 TB |
726 | /* if the user configured a CA certificate (or an invalid identity), |
727 | * we use the IP/hostname of the server */ | |
9486a2e5 | 728 | gateway = identification_create_from_string(ike.remote); |
e6fcc172 | 729 | loose_gateway_id = TRUE; |
0ed6b7a7 | 730 | } |
19e64e10 | 731 | DBG1(DBG_CFG, "using gateway identity '%Y'", gateway); |
7daf5226 | 732 | |
6dbce9c8 MW |
733 | /** |
734 | * Set up configurations | |
735 | */ | |
9486a2e5 | 736 | ike_cfg = ike_cfg_create(&ike); |
4ac68f02 D |
737 | |
738 | str = nm_setting_vpn_get_data_item(vpn, "proposal"); | |
739 | proposal = streq(str, "yes"); | |
740 | str = nm_setting_vpn_get_data_item(vpn, "ike"); | |
741 | if (proposal && str && strlen(str)) | |
742 | { | |
743 | enumerator = enumerator_create_token(str, ";", ""); | |
744 | while (enumerator->enumerate(enumerator, &str)) | |
745 | { | |
746 | prop = proposal_create_from_string(PROTO_IKE, str); | |
747 | if (!prop) | |
748 | { | |
749 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
750 | NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, | |
751 | "Invalid IKE proposal."); | |
752 | enumerator->destroy(enumerator); | |
753 | ike_cfg->destroy(ike_cfg); | |
754 | gateway->destroy(gateway); | |
4ac68f02 D |
755 | return FALSE; |
756 | } | |
757 | ike_cfg->add_proposal(ike_cfg, prop); | |
758 | } | |
759 | enumerator->destroy(enumerator); | |
760 | } | |
761 | else | |
762 | { | |
763 | ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); | |
764 | ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE)); | |
765 | } | |
2ba5dadb TB |
766 | |
767 | peer_cfg = peer_cfg_create(priv->name, ike_cfg, &peer); | |
101d26ba MW |
768 | if (virtual) |
769 | { | |
5538e290 TB |
770 | peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET)); |
771 | peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6)); | |
101d26ba | 772 | } |
bc3eda99 TB |
773 | |
774 | method = nm_setting_vpn_get_data_item(vpn, "method"); | |
775 | if (streq(method, "cert") || | |
776 | streq(method, "eap-tls") || | |
777 | streq(method, "key") || | |
778 | streq(method, "agent") || | |
779 | streq(method, "smartcard")) | |
780 | { | |
781 | if (!add_auth_cfg_cert (priv, vpn, peer_cfg, err)) | |
782 | { | |
783 | peer_cfg->destroy(peer_cfg); | |
784 | ike_cfg->destroy(ike_cfg); | |
785 | gateway->destroy(gateway); | |
786 | return FALSE; | |
787 | } | |
788 | } | |
789 | else if (streq(method, "eap") || | |
790 | streq(method, "psk")) | |
791 | { | |
792 | if (!add_auth_cfg_pw(priv, vpn, peer_cfg, err)) | |
793 | { | |
794 | peer_cfg->destroy(peer_cfg); | |
795 | ike_cfg->destroy(ike_cfg); | |
796 | gateway->destroy(gateway); | |
797 | return FALSE; | |
798 | } | |
799 | } | |
800 | else | |
801 | { | |
802 | g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, | |
803 | "Configuration parameters missing."); | |
804 | peer_cfg->destroy(peer_cfg); | |
805 | ike_cfg->destroy(ike_cfg); | |
806 | gateway->destroy(gateway); | |
807 | return FALSE; | |
808 | } | |
809 | ||
a44bb934 | 810 | auth = auth_cfg_create(); |
393e0167 | 811 | if (streq(method, "psk")) |
5ae822cf TB |
812 | { |
813 | auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); | |
814 | } | |
815 | else | |
816 | { | |
817 | auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); | |
818 | } | |
0ed6b7a7 | 819 | auth->add(auth, AUTH_RULE_IDENTITY, gateway); |
e6fcc172 | 820 | auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, loose_gateway_id); |
a44bb934 | 821 | peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE); |
7daf5226 | 822 | |
8a00a845 | 823 | child_cfg = child_cfg_create(priv->name, &child); |
4ac68f02 D |
824 | str = nm_setting_vpn_get_data_item(vpn, "esp"); |
825 | if (proposal && str && strlen(str)) | |
826 | { | |
827 | enumerator = enumerator_create_token(str, ";", ""); | |
828 | while (enumerator->enumerate(enumerator, &str)) | |
829 | { | |
830 | prop = proposal_create_from_string(PROTO_ESP, str); | |
831 | if (!prop) | |
832 | { | |
833 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
834 | NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, | |
835 | "Invalid ESP proposal."); | |
836 | enumerator->destroy(enumerator); | |
837 | child_cfg->destroy(child_cfg); | |
838 | peer_cfg->destroy(peer_cfg); | |
839 | return FALSE; | |
840 | } | |
841 | child_cfg->add_proposal(child_cfg, prop); | |
842 | } | |
843 | enumerator->destroy(enumerator); | |
844 | } | |
845 | else | |
846 | { | |
4ac68f02 | 847 | child_cfg->add_proposal(child_cfg, proposal_create_default_aead(PROTO_ESP)); |
33412158 | 848 | child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); |
4ac68f02 | 849 | } |
b81d8cd3 | 850 | ts = traffic_selector_create_dynamic(0, 0, 65535); |
6dbce9c8 | 851 | child_cfg->add_traffic_selector(child_cfg, TRUE, ts); |
04db34a3 T |
852 | str = nm_setting_vpn_get_data_item(vpn, "remote-ts"); |
853 | if (str && strlen(str)) | |
854 | { | |
855 | enumerator = enumerator_create_token(str, ";", ""); | |
856 | while (enumerator->enumerate(enumerator, &str)) | |
857 | { | |
858 | ts = traffic_selector_create_from_cidr((char*)str, 0, 0, 65535); | |
859 | if (!ts) | |
860 | { | |
861 | g_set_error(err, NM_VPN_PLUGIN_ERROR, | |
862 | NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, | |
863 | "Invalid remote traffic selector."); | |
864 | enumerator->destroy(enumerator); | |
865 | child_cfg->destroy(child_cfg); | |
866 | peer_cfg->destroy(peer_cfg); | |
867 | return FALSE; | |
868 | } | |
869 | child_cfg->add_traffic_selector(child_cfg, FALSE, ts); | |
870 | } | |
871 | enumerator->destroy(enumerator); | |
872 | } | |
873 | else | |
874 | { | |
875 | ts = traffic_selector_create_from_cidr("0.0.0.0/0", 0, 0, 65535); | |
876 | child_cfg->add_traffic_selector(child_cfg, FALSE, ts); | |
877 | ts = traffic_selector_create_from_cidr("::/0", 0, 0, 65535); | |
878 | child_cfg->add_traffic_selector(child_cfg, FALSE, ts); | |
879 | } | |
6dbce9c8 | 880 | peer_cfg->add_child_cfg(peer_cfg, child_cfg); |
7daf5226 | 881 | |
6dbce9c8 | 882 | /** |
bad99d5a | 883 | * Prepare IKE_SA |
6dbce9c8 MW |
884 | */ |
885 | ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager, | |
886 | peer_cfg); | |
7f6386af | 887 | peer_cfg->destroy(peer_cfg); |
3d54ae94 MW |
888 | if (!ike_sa) |
889 | { | |
3d54ae94 MW |
890 | g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, |
891 | "IKE version not supported."); | |
892 | return FALSE; | |
893 | } | |
7daf5226 | 894 | |
bad99d5a MW |
895 | /** |
896 | * Register listener, enable initiate-failure-detection hooks | |
897 | */ | |
69b80589 | 898 | priv->ike_sa = ike_sa; |
3d2f5ae0 TB |
899 | priv->listener.ike_state_change = _ike_state_change; |
900 | priv->listener.child_state_change = _child_state_change; | |
7daf5226 | 901 | |
bad99d5a MW |
902 | /** |
903 | * Initiate | |
904 | */ | |
3d54ae94 | 905 | child_cfg->get_ref(child_cfg); |
7f30e1ae | 906 | if (ike_sa->initiate(ike_sa, child_cfg, NULL) != SUCCESS) |
6dbce9c8 MW |
907 | { |
908 | charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); | |
7daf5226 | 909 | |
6dbce9c8 | 910 | g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, |
323f9f99 | 911 | "Initiating failed."); |
6dbce9c8 MW |
912 | return FALSE; |
913 | } | |
6dbce9c8 MW |
914 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); |
915 | return TRUE; | |
916 | } | |
917 | ||
918 | /** | |
7daf5226 | 919 | * NeedSecrets called from NM via DBUS |
6dbce9c8 | 920 | */ |
9a71b721 LR |
921 | static gboolean need_secrets(NMVpnServicePlugin *plugin, NMConnection *connection, |
922 | const char **setting_name, GError **error) | |
6dbce9c8 | 923 | { |
9a71b721 | 924 | NMSettingVpn *settings; |
bc3eda99 TB |
925 | const char *method, *cert_source, *path; |
926 | bool need_secret = FALSE; | |
7daf5226 | 927 | |
ec249871 MW |
928 | settings = NM_SETTING_VPN(nm_connection_get_setting(connection, |
929 | NM_TYPE_SETTING_VPN)); | |
d7625f09 | 930 | method = nm_setting_vpn_get_data_item(settings, "method"); |
aff26a62 | 931 | if (method) |
ec249871 | 932 | { |
bc3eda99 TB |
933 | if (streq(method, "cert") || |
934 | streq(method, "eap-tls") || | |
935 | streq(method, "key") || | |
936 | streq(method, "agent") || | |
937 | streq(method, "smartcard")) | |
aff26a62 | 938 | { |
bc3eda99 TB |
939 | cert_source = nm_setting_vpn_get_data_item(settings, "cert-source"); |
940 | if (!cert_source) | |
aff26a62 | 941 | { |
bc3eda99 | 942 | cert_source = method; |
aff26a62 | 943 | } |
bc3eda99 | 944 | if (streq(cert_source, "agent")) |
aff26a62 | 945 | { |
bc3eda99 | 946 | need_secret = !nm_setting_vpn_get_secret(settings, "agent"); |
aff26a62 | 947 | } |
bc3eda99 | 948 | else if (streq(cert_source, "smartcard")) |
1d03954a | 949 | { |
bc3eda99 TB |
950 | need_secret = !nm_setting_vpn_get_secret(settings, "password"); |
951 | } | |
952 | else | |
953 | { | |
954 | need_secret = TRUE; | |
955 | path = nm_setting_vpn_get_data_item(settings, "userkey"); | |
956 | if (path) | |
c489c588 | 957 | { |
bc3eda99 TB |
958 | private_key_t *key; |
959 | ||
960 | /* try to load/decrypt the private key */ | |
961 | key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, | |
962 | KEY_ANY, BUILD_FROM_FILE, path, BUILD_END); | |
963 | if (key) | |
964 | { | |
965 | key->destroy(key); | |
966 | need_secret = FALSE; | |
967 | } | |
968 | else if (nm_setting_vpn_get_secret(settings, "password")) | |
969 | { | |
970 | need_secret = FALSE; | |
971 | } | |
c489c588 | 972 | } |
1d03954a MW |
973 | } |
974 | } | |
bc3eda99 TB |
975 | else if (streq(method, "eap") || |
976 | streq(method, "psk")) | |
8bec0f51 | 977 | { |
bc3eda99 | 978 | need_secret = !nm_setting_vpn_get_secret(settings, "password"); |
8bec0f51 | 979 | } |
ec249871 | 980 | } |
aff26a62 | 981 | *setting_name = NM_SETTING_VPN_SETTING_NAME; |
bc3eda99 | 982 | return need_secret; |
6dbce9c8 MW |
983 | } |
984 | ||
985 | /** | |
9a71b721 | 986 | * The actual disconnection |
6dbce9c8 | 987 | */ |
9a71b721 | 988 | static gboolean do_disconnect(gpointer plugin) |
6dbce9c8 | 989 | { |
69b80589 | 990 | NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); |
6dbce9c8 MW |
991 | enumerator_t *enumerator; |
992 | ike_sa_t *ike_sa; | |
993 | u_int id; | |
7daf5226 | 994 | |
69b80589 | 995 | /* our ike_sa pointer might be invalid, lookup sa */ |
69c3eca0 MW |
996 | enumerator = charon->controller->create_ike_sa_enumerator( |
997 | charon->controller, TRUE); | |
6dbce9c8 MW |
998 | while (enumerator->enumerate(enumerator, &ike_sa)) |
999 | { | |
69b80589 | 1000 | if (priv->ike_sa == ike_sa) |
6dbce9c8 MW |
1001 | { |
1002 | id = ike_sa->get_unique_id(ike_sa); | |
1003 | enumerator->destroy(enumerator); | |
7b729097 | 1004 | charon->controller->terminate_ike(charon->controller, id, FALSE, |
8e3f14ba | 1005 | controller_cb_empty, NULL, 0); |
d5d83756 TB |
1006 | |
1007 | /* clear secrets as we are asked for new secrets (where we'd find | |
1008 | * the cached secrets from earlier connections) before we clear | |
1009 | * them in connect() */ | |
1010 | priv->creds->clear(priv->creds); | |
9a71b721 | 1011 | return FALSE; |
6dbce9c8 MW |
1012 | } |
1013 | } | |
1014 | enumerator->destroy(enumerator); | |
7daf5226 | 1015 | |
9a71b721 | 1016 | g_debug("Connection not found."); |
6dbce9c8 MW |
1017 | return FALSE; |
1018 | } | |
1019 | ||
9a71b721 LR |
1020 | /** |
1021 | * Disconnect called from NM via DBUS | |
1022 | */ | |
1023 | static gboolean disconnect(NMVpnServicePlugin *plugin, GError **err) | |
1024 | { | |
9a71b721 LR |
1025 | /* enqueue the actual disconnection, because we may be called in |
1026 | * response to a listener_t callback and the SA enumeration would | |
1027 | * possibly deadlock. */ | |
1028 | g_idle_add(do_disconnect, plugin); | |
1029 | ||
1030 | return TRUE; | |
1031 | } | |
1032 | ||
6dbce9c8 MW |
1033 | /** |
1034 | * Initializer | |
1035 | */ | |
1036 | static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin) | |
1037 | { | |
69b80589 | 1038 | NMStrongswanPluginPrivate *priv; |
7daf5226 | 1039 | |
69b80589 | 1040 | priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); |
9a71b721 | 1041 | priv->plugin = NM_VPN_SERVICE_PLUGIN(plugin); |
b64f3336 | 1042 | memset(&priv->listener, 0, sizeof(listener_t)); |
3d2f5ae0 TB |
1043 | priv->listener.child_updown = _child_updown; |
1044 | priv->listener.ike_rekey = _ike_rekey; | |
1045 | priv->listener.ike_reestablish_pre = _ike_reestablish_pre; | |
1046 | priv->listener.ike_reestablish_post = _ike_reestablish_post; | |
5575aaf5 | 1047 | charon->bus->add_listener(charon->bus, &priv->listener); |
aa3d5bf7 | 1048 | priv->tun = tun_device_create(NULL); |
c15eea73 TB |
1049 | priv->name = NULL; |
1050 | } | |
1051 | ||
aa3d5bf7 TB |
1052 | /** |
1053 | * Destructor | |
1054 | */ | |
1055 | static void nm_strongswan_plugin_dispose(GObject *obj) | |
1056 | { | |
1057 | NMStrongswanPlugin *plugin; | |
1058 | NMStrongswanPluginPrivate *priv; | |
1059 | ||
1060 | plugin = NM_STRONGSWAN_PLUGIN(obj); | |
1061 | priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); | |
1062 | if (priv->tun) | |
1063 | { | |
1064 | priv->tun->destroy(priv->tun); | |
1065 | priv->tun = NULL; | |
1066 | } | |
1067 | G_OBJECT_CLASS (nm_strongswan_plugin_parent_class)->dispose (obj); | |
1068 | } | |
1069 | ||
6dbce9c8 MW |
1070 | /** |
1071 | * Class constructor | |
1072 | */ | |
1073 | static void nm_strongswan_plugin_class_init( | |
1074 | NMStrongswanPluginClass *strongswan_class) | |
1075 | { | |
9a71b721 | 1076 | NMVpnServicePluginClass *parent_class = NM_VPN_SERVICE_PLUGIN_CLASS(strongswan_class); |
7daf5226 | 1077 | |
6dbce9c8 MW |
1078 | parent_class->connect = connect_; |
1079 | parent_class->need_secrets = need_secrets; | |
1080 | parent_class->disconnect = disconnect; | |
aa3d5bf7 | 1081 | G_OBJECT_CLASS(strongswan_class)->dispose = nm_strongswan_plugin_dispose; |
6dbce9c8 MW |
1082 | } |
1083 | ||
1084 | /** | |
1085 | * Object constructor | |
1086 | */ | |
3ab0e8a0 MW |
1087 | NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds, |
1088 | nm_handler_t *handler) | |
6dbce9c8 | 1089 | { |
9a71b721 LR |
1090 | GError *error = NULL; |
1091 | ||
1092 | NMStrongswanPlugin *plugin = (NMStrongswanPlugin *)g_initable_new ( | |
ec249871 | 1093 | NM_TYPE_STRONGSWAN_PLUGIN, |
9a71b721 LR |
1094 | NULL, |
1095 | &error, | |
1096 | NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, NM_DBUS_SERVICE_STRONGSWAN, | |
ec249871 | 1097 | NULL); |
9a71b721 | 1098 | |
1b9f6c24 MW |
1099 | if (plugin) |
1100 | { | |
69b80589 | 1101 | NMStrongswanPluginPrivate *priv; |
7daf5226 | 1102 | |
c15eea73 | 1103 | /* the rest of the initialization happened in _init above */ |
69b80589 MW |
1104 | priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); |
1105 | priv->creds = creds; | |
1106 | priv->handler = handler; | |
1b9f6c24 | 1107 | } |
9a71b721 LR |
1108 | else |
1109 | { | |
1110 | g_warning ("Failed to initialize a plugin instance: %s", error->message); | |
1111 | g_error_free (error); | |
1112 | } | |
1113 | ||
ec249871 | 1114 | return plugin; |
6dbce9c8 | 1115 | } |