2 * Copyright (C) 2010 Tobias Brunner
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
20 #include "maemo_service.h"
23 #include <credentials/sets/mem_cred.h>
24 #include <processing/jobs/callback_job.h>
26 #define OSSO_STATUS_NAME "status"
27 #define OSSO_STATUS_SERVICE "org.strongswan."OSSO_STATUS_NAME
28 #define OSSO_STATUS_OBJECT "/org/strongswan/"OSSO_STATUS_NAME
29 #define OSSO_STATUS_IFACE "org.strongswan."OSSO_STATUS_NAME
31 #define OSSO_CHARON_NAME "charon"
32 #define OSSO_CHARON_SERVICE "org.strongswan."OSSO_CHARON_NAME
33 #define OSSO_CHARON_OBJECT "/org/strongswan/"OSSO_CHARON_NAME
34 #define OSSO_CHARON_IFACE "org.strongswan."OSSO_CHARON_NAME
36 #define MAEMO_COMMON_CA_DIR "/etc/certs/common-ca"
37 #define MAEMO_USER_CA_DIR "/home/user/.maemosec-certs/wifi-ca"
38 /* there is also an smime-ca and an ssl-ca sub-directory and the same for
39 * ...-user, which store end user/server certificates */
42 VPN_STATUS_DISCONNECTED
,
43 VPN_STATUS_CONNECTING
,
45 VPN_STATUS_AUTH_FAILED
,
46 VPN_STATUS_CONNECTION_FAILED
,
49 typedef struct private_maemo_service_t private_maemo_service_t
;
52 * private data of maemo service
54 struct private_maemo_service_t
{
59 maemo_service_t
public;
67 * Glib main loop for a thread, handles DBUS calls
74 osso_context_t
*context
;
82 * Status of the current connection
87 * Name of the current connection
93 static gint
change_status(private_maemo_service_t
*this, int status
)
97 this->status
= status
;
98 res
= osso_rpc_run (this->context
, OSSO_STATUS_SERVICE
, OSSO_STATUS_OBJECT
,
99 OSSO_STATUS_IFACE
, "StatusChanged", &retval
,
100 DBUS_TYPE_INT32
, status
,
105 METHOD(listener_t
, ike_updown
, bool,
106 private_maemo_service_t
*this, ike_sa_t
*ike_sa
, bool up
)
108 /* this callback is only registered during initiation, so if the IKE_SA
109 * goes down we assume an authentication error */
110 if (this->ike_sa
== ike_sa
&& !up
)
112 change_status(this, VPN_STATUS_AUTH_FAILED
);
118 METHOD(listener_t
, ike_state_change
, bool,
119 private_maemo_service_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
121 /* this call back is only registered during initiation */
122 if (this->ike_sa
== ike_sa
&& state
== IKE_DESTROYING
)
124 change_status(this, VPN_STATUS_CONNECTION_FAILED
);
130 METHOD(listener_t
, child_updown
, bool,
131 private_maemo_service_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
134 if (this->ike_sa
== ike_sa
)
138 /* disable hooks registered to catch initiation failures */
139 this->public.listener
.ike_updown
= NULL
;
140 this->public.listener
.ike_state_change
= NULL
;
141 change_status(this, VPN_STATUS_CONNECTED
);
145 change_status(this, VPN_STATUS_CONNECTION_FAILED
);
152 METHOD(listener_t
, ike_rekey
, bool,
153 private_maemo_service_t
*this, ike_sa_t
*old
, ike_sa_t
*new)
155 if (this->ike_sa
== old
)
163 * load all CA certificates in the given directory
165 static void load_ca_dir(private_maemo_service_t
*this, char *dir
)
167 enumerator_t
*enumerator
;
171 enumerator
= enumerator_create_directory(dir
);
174 while (enumerator
->enumerate(enumerator
, &rel
, &abs
, &st
))
178 if (S_ISREG(st
.st_mode
))
181 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
,
182 CERT_X509
, BUILD_FROM_FILE
, abs
,
186 DBG1(DBG_CFG
, "loading CA certificate '%s' failed",
190 DBG2(DBG_CFG
, "loaded CA certificate '%Y'",
191 cert
->get_subject(cert
));
192 this->creds
->add_cert(this->creds
, TRUE
, cert
);
196 enumerator
->destroy(enumerator
);
200 static void disconnect(private_maemo_service_t
*this)
210 /* avoid status updates, as this is called from the Glib main loop */
211 charon
->bus
->remove_listener(charon
->bus
, &this->public.listener
);
213 ike_sa
= charon
->ike_sa_manager
->checkout_by_name(charon
->ike_sa_manager
,
214 this->current
, FALSE
);
217 id
= ike_sa
->get_unique_id(ike_sa
);
218 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
219 charon
->controller
->terminate_ike(charon
->controller
, id
,
222 this->current
= (g_free(this->current
), NULL
);
223 this->status
= VPN_STATUS_DISCONNECTED
;
226 static gboolean
initiate_connection(private_maemo_service_t
*this,
230 gchar
*hostname
= NULL
, *cacert
= NULL
, *username
= NULL
, *password
= NULL
;
231 identification_t
*gateway
= NULL
, *user
= NULL
;
234 peer_cfg_t
*peer_cfg
;
235 child_cfg_t
*child_cfg
;
236 traffic_selector_t
*ts
;
239 lifetime_cfg_t lifetime
= {
241 .life
= 10800, /* 3h */
242 .rekey
= 10200, /* 2h50min */
243 .jitter
= 300 /* 5min */
247 if (this->status
== VPN_STATUS_CONNECTED
||
248 this->status
== VPN_STATUS_CONNECTING
)
250 DBG1(DBG_CFG
, "currently connected to '%s', disconnecting first",
255 if (arguments
->len
!= 5)
257 DBG1(DBG_CFG
, "wrong number of arguments: %d", arguments
->len
);
261 for (i
= 0; i
< arguments
->len
; i
++)
263 osso_rpc_t
*arg
= &g_array_index(arguments
, osso_rpc_t
, i
);
264 if (arg
->type
!= DBUS_TYPE_STRING
)
266 DBG1(DBG_CFG
, "invalid argument [%d]: %d", i
, arg
->type
);
272 this->current
= (g_free(this->current
), NULL
);
273 this->current
= g_strdup(arg
->value
.s
);
275 case 1: /* hostname */
276 hostname
= arg
->value
.s
;
278 case 2: /* CA certificate path */
279 cacert
= arg
->value
.s
;
281 case 3: /* username */
282 username
= arg
->value
.s
;
284 case 4: /* password */
285 password
= arg
->value
.s
;
290 DBG1(DBG_CFG
, "received initiate for connection '%s'", this->current
);
292 this->creds
->clear(this->creds
);
294 if (cacert
&& !streq(cacert
, ""))
296 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
297 BUILD_FROM_FILE
, cacert
, BUILD_END
);
300 this->creds
->add_cert(this->creds
, TRUE
, cert
);
304 DBG1(DBG_CFG
, "failed to load CA certificate");
306 /* if this is a server cert we could use the cert subject as id */
310 load_ca_dir(this, MAEMO_COMMON_CA_DIR
);
311 load_ca_dir(this, MAEMO_USER_CA_DIR
);
314 gateway
= identification_create_from_string(hostname
);
315 DBG1(DBG_CFG
, "using CA certificate, gateway identitiy '%Y'", gateway
);
318 shared_key_t
*shared_key
;
319 chunk_t secret
= chunk_create(password
, strlen(password
));
320 user
= identification_create_from_string(username
);
321 shared_key
= shared_key_create(SHARED_EAP
, chunk_clone(secret
));
322 this->creds
->add_shared(this->creds
, shared_key
, user
->clone(user
),
326 ike_cfg
= ike_cfg_create(TRUE
, FALSE
, "0.0.0.0", IKEV2_UDP_PORT
,
327 hostname
, IKEV2_UDP_PORT
);
328 ike_cfg
->add_proposal(ike_cfg
, proposal_create_default(PROTO_IKE
));
330 peer_cfg
= peer_cfg_create(this->current
, 2, ike_cfg
, CERT_SEND_IF_ASKED
,
331 UNIQUE_REPLACE
, 1, /* keyingtries */
332 36000, 0, /* rekey 10h, reauth none */
333 600, 600, /* jitter, over 10min */
334 TRUE
, 0, /* mobike, DPD */
335 host_create_from_string("0.0.0.0", 0) /* virt */,
336 NULL
, FALSE
, NULL
, NULL
); /* pool, mediation */
338 auth
= auth_cfg_create();
339 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_EAP
);
340 auth
->add(auth
, AUTH_RULE_IDENTITY
, user
);
341 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, TRUE
);
342 auth
= auth_cfg_create();
343 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_PUBKEY
);
344 auth
->add(auth
, AUTH_RULE_IDENTITY
, gateway
);
345 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, FALSE
);
347 child_cfg
= child_cfg_create(this->current
, &lifetime
, NULL
/* updown */,
348 TRUE
, MODE_TUNNEL
, ACTION_NONE
, ACTION_NONE
,
349 ACTION_NONE
, FALSE
, 0, 0, NULL
, NULL
, 0);
350 child_cfg
->add_proposal(child_cfg
, proposal_create_default(PROTO_ESP
));
351 ts
= traffic_selector_create_dynamic(0, 0, 65535);
352 child_cfg
->add_traffic_selector(child_cfg
, TRUE
, ts
);
353 ts
= traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE
, "0.0.0.0",
354 0, "255.255.255.255", 65535);
355 child_cfg
->add_traffic_selector(child_cfg
, FALSE
, ts
);
356 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
);
357 /* get an additional reference because initiate consumes one */
358 child_cfg
->get_ref(child_cfg
);
360 /* get us an IKE_SA */
361 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
363 if (!ike_sa
->get_peer_cfg(ike_sa
))
365 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
367 peer_cfg
->destroy(peer_cfg
);
369 /* store the IKE_SA, so we can track its progress */
370 this->ike_sa
= ike_sa
;
371 this->status
= VPN_STATUS_CONNECTING
;
372 this->public.listener
.ike_updown
= _ike_updown
;
373 this->public.listener
.ike_state_change
= _ike_state_change
;
374 charon
->bus
->add_listener(charon
->bus
, &this->public.listener
);
376 if (ike_sa
->initiate(ike_sa
, child_cfg
, 0, NULL
, NULL
) != SUCCESS
)
378 DBG1(DBG_CFG
, "failed to initiate tunnel");
379 charon
->bus
->remove_listener(charon
->bus
, &this->public.listener
);
380 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
382 this->status
= VPN_STATUS_CONNECTION_FAILED
;
385 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
390 * Callback for libosso dbus wrapper
392 static gint
dbus_req_handler(const gchar
*interface
, const gchar
*method
,
393 GArray
*arguments
, private_maemo_service_t
*this,
396 if (streq(method
, "Start"))
397 { /* void start (void), dummy function to start charon as root */
400 else if (streq(method
, "Connect"))
401 { /* bool connect (name, host, cert, user, pass) */
402 retval
->value
.b
= initiate_connection(this, arguments
);
403 retval
->type
= DBUS_TYPE_BOOLEAN
;
405 else if (streq(method
, "Disconnect"))
406 { /* void disconnect (void) */
417 * Main loop to handle D-BUS messages.
419 static job_requeue_t
run(private_maemo_service_t
*this)
421 this->loop
= g_main_loop_new(NULL
, FALSE
);
422 g_main_loop_run(this->loop
);
423 return JOB_REQUEUE_NONE
;
426 METHOD(maemo_service_t
, destroy
, void,
427 private_maemo_service_t
*this)
431 if (g_main_loop_is_running(this->loop
))
433 g_main_loop_quit(this->loop
);
435 g_main_loop_unref(this->loop
);
439 osso_rpc_unset_cb_f(this->context
,
443 (osso_rpc_cb_f
*)dbus_req_handler
,
445 osso_deinitialize(this->context
);
447 charon
->bus
->remove_listener(charon
->bus
, &this->public.listener
);
448 lib
->credmgr
->remove_set(lib
->credmgr
, &this->creds
->set
);
449 this->creds
->destroy(this->creds
);
450 this->current
= (g_free(this->current
), NULL
);
457 maemo_service_t
*maemo_service_create()
459 osso_return_t result
;
460 private_maemo_service_t
*this;
465 .ike_updown
= _ike_updown
,
466 .ike_state_change
= _ike_state_change
,
467 .child_updown
= _child_updown
,
468 .ike_rekey
= _ike_rekey
,
472 .creds
= mem_cred_create(),
475 lib
->credmgr
->add_set(lib
->credmgr
, &this->creds
->set
);
477 this->context
= osso_initialize(OSSO_CHARON_SERVICE
, "0.0.1", TRUE
, NULL
);
480 DBG1(DBG_CFG
, "failed to initialize OSSO context");
485 result
= osso_rpc_set_cb_f(this->context
,
489 (osso_rpc_cb_f
*)dbus_req_handler
,
491 if (result
!= OSSO_OK
)
493 DBG1(DBG_CFG
, "failed to set D-BUS callback (%d)", result
);
499 if (!g_thread_supported())
504 lib
->processor
->queue_job(lib
->processor
,
505 (job_t
*)callback_job_create_with_prio((callback_job_cb_t
)run
,
506 this, NULL
, NULL
, JOB_PRIO_CRITICAL
));
508 return &this->public;