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