root/trunk/src/charon/plugins/nm/nm_service.c

Revision 4485, 12.6 kB (checked in by martin, 2 months ago)

including a "none" tundev to make NM happy

Line 
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
Note: See TracBrowser for help on using the browser.