always encode EAP usernames as ID_KEY_ID
[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_encoding(ID_KEY_ID,
227 chunk_create(str, strlen(str)));
228 str = g_hash_table_lookup(settings->secrets, "password");
229 creds->set_username_password(creds, user, str);
230 }
231 }
232
233 if (auth_class == AUTH_CLASS_PUBKEY)
234 {
235 /* ... or certificate/private key authenitcation */
236 str = g_hash_table_lookup(settings->data, "usercert");
237 if (str)
238 {
239 public_key_t *public;
240 private_key_t *private = NULL;
241
242 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
243 BUILD_FROM_FILE, str, BUILD_END);
244
245 /* try agent */
246 str = g_hash_table_lookup(settings->secrets, "agent");
247 if (agent && str && cert)
248 {
249 public = cert->get_public_key(cert);
250 if (public)
251 {
252 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
253 public->get_type(public),
254 BUILD_AGENT_SOCKET, str,
255 BUILD_PUBLIC_KEY, public,
256 BUILD_END);
257 public->destroy(public);
258 }
259 }
260 /* ... or key file */
261 str = g_hash_table_lookup(settings->data, "userkey");
262 if (!agent && str && cert)
263 {
264 chunk_t secret, chunk;
265 bool pgp = FALSE;
266
267 secret.ptr = g_hash_table_lookup(settings->secrets, "password");
268 if (secret.ptr)
269 {
270 secret.len = strlen(secret.ptr);
271 }
272 if (pem_asn1_load_file(str, &secret, &chunk, &pgp))
273 {
274 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
275 KEY_RSA, BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
276 free(chunk.ptr);
277 }
278 }
279 if (private)
280 {
281 user = cert->get_subject(cert);
282 user = user->clone(user);
283 creds->set_cert_and_key(creds, cert, private);
284 }
285 else
286 {
287 DESTROY_IF(cert);
288 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
289 "Loading user certificate/private key failed.");
290 return FALSE;
291 }
292 }
293 }
294
295 if (!user)
296 {
297 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
298 "Configuration parameters missing.");
299 return FALSE;
300 }
301
302 /**
303 * Set up configurations
304 */
305 ike_cfg = ike_cfg_create(TRUE, encap, "0.0.0.0", address);
306 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
307 peer_cfg = peer_cfg_create(CONFIG_NAME, 2, ike_cfg,
308 user, gateway->clone(gateway),
309 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
310 36000, 0, /* rekey 10h, reauth none */
311 600, 600, /* jitter, over 10min */
312 TRUE, 0, /* mobike, DPD */
313 virtual ? host_create_from_string("0.0.0.0", 0) : NULL,
314 NULL, FALSE, NULL, NULL); /* pool, mediation */
315 auth = peer_cfg->get_auth(peer_cfg);
316 auth->add_item(auth, AUTHN_AUTH_CLASS, &auth_class);
317 child_cfg = child_cfg_create(CONFIG_NAME,
318 10800, 10200, /* lifetime 3h, rekey 2h50min */
319 300, /* jitter 5min */
320 NULL, TRUE, MODE_TUNNEL, /* updown, hostaccess */
321 ACTION_NONE, ACTION_RESTART, ipcomp);
322 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
323 ts = traffic_selector_create_dynamic(0, 0, 65535);
324 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
325 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
326 "0.0.0.0", 0,
327 "255.255.255.255", 65535);
328 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
329 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
330
331 /**
332 * Start to initiate
333 */
334 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
335 peer_cfg);
336 if (!ike_sa->get_peer_cfg(ike_sa))
337 {
338 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
339 }
340 else
341 {
342 peer_cfg->destroy(peer_cfg);
343 }
344 if (ike_sa->initiate(ike_sa, child_cfg) != SUCCESS)
345 {
346 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
347
348 g_set_error(err, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
349 "Initiating failed.");
350 return FALSE;
351 }
352
353 /**
354 * Register listener
355 */
356 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->ike_sa = ike_sa;
357 charon->bus->add_listener(charon->bus,
358 &NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->listener);
359 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
360 return TRUE;
361 }
362
363 /**
364 * NeedSecrets called from NM via DBUS
365 */
366 static gboolean need_secrets(NMVPNPlugin *plugin, NMConnection *connection,
367 char **setting_name, GError **error)
368 {
369 NMSettingVPN *settings;
370 char *method, *path;
371 chunk_t secret = chunk_empty, key;
372 bool pgp = FALSE;
373
374 settings = NM_SETTING_VPN(nm_connection_get_setting(connection,
375 NM_TYPE_SETTING_VPN));
376 method = g_hash_table_lookup(settings->data, "method");
377 if (method)
378 {
379 if (streq(method, "eap"))
380 {
381 if (g_hash_table_lookup(settings->secrets, "password"))
382 {
383 return FALSE;
384 }
385 }
386 else if (streq(method, "agent"))
387 {
388 if (g_hash_table_lookup(settings->secrets, "agent"))
389 {
390 return FALSE;
391 }
392 }
393 else if (streq(method, "key"))
394 {
395 path = g_hash_table_lookup(settings->data, "userkey");
396 if (path)
397 {
398 secret.ptr = g_hash_table_lookup(settings->secrets, "password");
399 if (secret.ptr)
400 {
401 secret.len = strlen(secret.ptr);
402 }
403 if (pem_asn1_load_file(path, &secret, &key, &pgp))
404 {
405 free(key.ptr);
406 return FALSE;
407 }
408 }
409 }
410 }
411 *setting_name = NM_SETTING_VPN_SETTING_NAME;
412 return TRUE;
413 }
414
415 /**
416 * Disconnect called from NM via DBUS
417 */
418 static gboolean disconnect(NMVPNPlugin *plugin, GError **err)
419 {
420 enumerator_t *enumerator;
421 ike_sa_t *ike_sa;
422 u_int id;
423
424 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
425 while (enumerator->enumerate(enumerator, &ike_sa))
426 {
427 if (streq(CONFIG_NAME, ike_sa->get_name(ike_sa)))
428 {
429 id = ike_sa->get_unique_id(ike_sa);
430 enumerator->destroy(enumerator);
431 charon->controller->terminate_ike(charon->controller, id,
432 controller_cb_empty, NULL);
433 return TRUE;
434 }
435 }
436 enumerator->destroy(enumerator);
437 return FALSE;
438 }
439
440 /**
441 * Initializer
442 */
443 static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin)
444 {
445 NMStrongswanPluginPrivate *private;
446
447 private = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
448 private->plugin = NM_VPN_PLUGIN(plugin);
449 memset(&private->listener.log, 0, sizeof(listener_t));
450 private->listener.ike_state_change = ike_state_change;
451 private->listener.child_state_change = child_state_change;
452 }
453
454 /**
455 * Class constructor
456 */
457 static void nm_strongswan_plugin_class_init(
458 NMStrongswanPluginClass *strongswan_class)
459 {
460 NMVPNPluginClass *parent_class = NM_VPN_PLUGIN_CLASS(strongswan_class);
461
462 g_type_class_add_private(G_OBJECT_CLASS(strongswan_class),
463 sizeof(NMStrongswanPluginPrivate));
464 parent_class->connect = connect_;
465 parent_class->need_secrets = need_secrets;
466 parent_class->disconnect = disconnect;
467 }
468
469 /**
470 * Object constructor
471 */
472 NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds)
473 {
474 NMStrongswanPlugin *plugin = (NMStrongswanPlugin *)g_object_new (
475 NM_TYPE_STRONGSWAN_PLUGIN,
476 NM_VPN_PLUGIN_DBUS_SERVICE_NAME, NM_DBUS_SERVICE_STRONGSWAN,
477 NULL);
478 if (plugin)
479 {
480 NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin)->creds = creds;
481 }
482 return plugin;
483 }
484