diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2010-08-09 08:09:54 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2010-08-09 08:09:54 +0000 |
commit | b8064f4099997a9e2179f3ad4ace605f5ccac3a1 (patch) | |
tree | 81778e976b476374c48b4fe83d084b986b890421 /src/libcharon/plugins/android | |
parent | 1ac70afcc1f7d6d2738a34308810719b0976d29f (diff) | |
download | vyos-strongswan-b8064f4099997a9e2179f3ad4ace605f5ccac3a1.tar.gz vyos-strongswan-b8064f4099997a9e2179f3ad4ace605f5ccac3a1.zip |
[svn-upgrade] new version strongswan (4.4.1)
Diffstat (limited to 'src/libcharon/plugins/android')
-rw-r--r-- | src/libcharon/plugins/android/Makefile.am | 5 | ||||
-rw-r--r-- | src/libcharon/plugins/android/Makefile.in | 13 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_creds.c | 294 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_creds.h | 73 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_handler.c | 4 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_logger.c | 96 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_logger.h | 52 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_plugin.c | 41 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_service.c | 385 | ||||
-rw-r--r-- | src/libcharon/plugins/android/android_service.h | 54 |
10 files changed, 1009 insertions, 8 deletions
diff --git a/src/libcharon/plugins/android/Makefile.am b/src/libcharon/plugins/android/Makefile.am index e8423589c..b922ef4af 100644 --- a/src/libcharon/plugins/android/Makefile.am +++ b/src/libcharon/plugins/android/Makefile.am @@ -12,7 +12,10 @@ endif libstrongswan_android_la_SOURCES = \ android_plugin.c android_plugin.h \ - android_handler.c android_handler.h + android_service.c android_service.h \ + android_handler.c android_handler.h \ + android_logger.c android_logger.h \ + android_creds.c android_creds.h libstrongswan_android_la_LDFLAGS = -module -avoid-version libstrongswan_android_la_LIBADD = -lcutils diff --git a/src/libcharon/plugins/android/Makefile.in b/src/libcharon/plugins/android/Makefile.in index 9f12a9c75..6e4903ee1 100644 --- a/src/libcharon/plugins/android/Makefile.in +++ b/src/libcharon/plugins/android/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -75,7 +75,8 @@ am__installdirs = "$(DESTDIR)$(plugindir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES) libstrongswan_android_la_DEPENDENCIES = am_libstrongswan_android_la_OBJECTS = android_plugin.lo \ - android_handler.lo + android_service.lo android_handler.lo android_logger.lo \ + android_creds.lo libstrongswan_android_la_OBJECTS = \ $(am_libstrongswan_android_la_OBJECTS) libstrongswan_android_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -265,7 +266,10 @@ AM_CFLAGS = -rdynamic @MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-android.la libstrongswan_android_la_SOURCES = \ android_plugin.c android_plugin.h \ - android_handler.c android_handler.h + android_service.c android_service.h \ + android_handler.c android_handler.h \ + android_logger.c android_logger.h \ + android_creds.c android_creds.h libstrongswan_android_la_LDFLAGS = -module -avoid-version libstrongswan_android_la_LIBADD = -lcutils @@ -352,8 +356,11 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/android_creds.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/android_handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/android_logger.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/android_plugin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/android_service.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/src/libcharon/plugins/android/android_creds.c b/src/libcharon/plugins/android/android_creds.c new file mode 100644 index 000000000..aa7fc6f92 --- /dev/null +++ b/src/libcharon/plugins/android/android_creds.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2010 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <keystore_get.h> + +#include "android_creds.h" + +#include <daemon.h> +#include <threading/rwlock.h> + +typedef struct private_android_creds_t private_android_creds_t; + +/** + * Private data of an android_creds_t object + */ +struct private_android_creds_t { + + /** + * Public interface + */ + android_creds_t public; + + /** + * List of trusted certificates, certificate_t* + */ + linked_list_t *certs; + + /** + * User name (ID) + */ + identification_t *user; + + /** + * User password + */ + char *pass; + + /** + * read/write lock + */ + rwlock_t *lock; + +}; + +/** + * Certificate enumerator data + */ +typedef struct { + private_android_creds_t *this; + key_type_t key; + identification_t *id; +} cert_data_t; + +/** + * Filter function for certificates enumerator + */ +static bool cert_filter(cert_data_t *data, certificate_t **in, + certificate_t **out) +{ + certificate_t *cert = *in; + public_key_t *public; + + public = cert->get_public_key(cert); + if (!public) + { + return FALSE; + } + if (data->key != KEY_ANY && public->get_type(public) != data->key) + { + public->destroy(public); + return FALSE; + } + if (data->id && data->id->get_type(data->id) == ID_KEY_ID && + public->has_fingerprint(public, data->id->get_encoding(data->id))) + { + public->destroy(public); + *out = cert; + return TRUE; + } + public->destroy(public); + if (data->id && !cert->has_subject(cert, data->id)) + { + return FALSE; + } + *out = cert; + return TRUE; +} + +/** + * Destroy certificate enumerator data + */ +static void cert_data_destroy(cert_data_t *this) +{ + this->this->lock->unlock(this->this->lock); + free(this); +} + +METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, + private_android_creds_t *this, certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + if (cert == CERT_X509 || cert == CERT_ANY) + { + cert_data_t *data; + this->lock->read_lock(this->lock); + INIT(data, .this = this, .id = id, .key = key); + return enumerator_create_filter( + this->certs->create_enumerator(this->certs), + (void*)cert_filter, data, (void*)cert_data_destroy); + } + return NULL; +} + +/** + * Shared key enumerator implementation + */ +typedef struct { + enumerator_t public; + private_android_creds_t *this; + shared_key_t *key; + bool done; +} shared_enumerator_t; + +METHOD(enumerator_t, shared_enumerate, bool, + shared_enumerator_t *this, shared_key_t **key, id_match_t *me, + id_match_t *other) +{ + if (this->done) + { + return FALSE; + } + *key = this->key; + *me = ID_MATCH_PERFECT; + *other = ID_MATCH_ANY; + this->done = TRUE; + return TRUE; +} + +METHOD(enumerator_t, shared_destroy, void, + shared_enumerator_t *this) +{ + this->key->destroy(this->key); + this->this->lock->unlock(this->this->lock); + free(this); +} + +METHOD(credential_set_t, create_shared_enumerator, enumerator_t*, + private_android_creds_t *this, shared_key_type_t type, + identification_t *me, identification_t *other) +{ + shared_enumerator_t *enumerator; + + this->lock->read_lock(this->lock); + + if (!this->user || !this->pass) + { + this->lock->unlock(this->lock); + return NULL; + } + if (type != SHARED_EAP && type != SHARED_IKE) + { + this->lock->unlock(this->lock); + return NULL; + } + if (me && !me->equals(me, this->user)) + { + this->lock->unlock(this->lock); + return NULL; + } + + INIT(enumerator, + .public = { + .enumerate = (void*)_shared_enumerate, + .destroy = _shared_destroy, + }, + .this = this, + .done = FALSE, + .key = shared_key_create(type, chunk_clone(chunk_create(this->pass, + strlen(this->pass)))), + ); + return &enumerator->public; +} + +METHOD(android_creds_t, add_certificate, bool, + private_android_creds_t *this, char *name) +{ + certificate_t *cert = NULL; + bool status = FALSE; + chunk_t chunk; +#ifdef KEYSTORE_MESSAGE_SIZE + /* most current interface, the eclair interface (without key length) is + * currently not supported */ + char value[KEYSTORE_MESSAGE_SIZE]; + chunk.ptr = value; + chunk.len = keystore_get(name, strlen(name), chunk.ptr); + if (chunk.len > 0) +#else + /* 1.6 interface, allocates memory */ + chunk.ptr = keystore_get(name, &chunk.len); + if (chunk.ptr) +#endif /* KEYSTORE_MESSAGE_SIZE */ + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_PEM, chunk, BUILD_END); + if (cert) + { + this->lock->write_lock(this->lock); + this->certs->insert_last(this->certs, cert); + this->lock->unlock(this->lock); + status = TRUE; + } +#ifndef KEYSTORE_MESSAGE_SIZE + free(chunk.ptr); +#endif /* KEYSTORE_MESSAGE_SIZE */ + } + return status; +} + +METHOD(android_creds_t, set_username_password, void, + private_android_creds_t *this, identification_t *id, char *password) +{ + this->lock->write_lock(this->lock); + DESTROY_IF(this->user); + this->user = id->clone(id); + free(this->pass); + this->pass = password ? strdup(password) : NULL; + this->lock->unlock(this->lock); +} + +METHOD(android_creds_t, clear, void, + private_android_creds_t *this) +{ + certificate_t *cert; + this->lock->write_lock(this->lock); + while (this->certs->remove_last(this->certs, (void**)&cert) == SUCCESS) + { + cert->destroy(cert); + } + DESTROY_IF(this->user); + free(this->pass); + this->user = NULL; + this->pass = NULL; + this->lock->unlock(this->lock); +} + +METHOD(android_creds_t, destroy, void, + private_android_creds_t *this) +{ + clear(this); + this->certs->destroy(this->certs); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Described in header. + */ +android_creds_t *android_creds_create() +{ + private_android_creds_t *this; + + INIT(this, + .public = { + .set = { + .create_cert_enumerator = _create_cert_enumerator, + .create_shared_enumerator = _create_shared_enumerator, + .create_private_enumerator = (void*)return_null, + .create_cdp_enumerator = (void*)return_null, + .cache_cert = (void*)nop, + }, + .add_certificate = _add_certificate, + .set_username_password = _set_username_password, + .clear = _clear, + .destroy = _destroy, + }, + .certs = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} + diff --git a/src/libcharon/plugins/android/android_creds.h b/src/libcharon/plugins/android/android_creds.h new file mode 100644 index 000000000..0f7b8e0ea --- /dev/null +++ b/src/libcharon/plugins/android/android_creds.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup android_creds android_creds + * @{ @ingroup android + */ + +#ifndef ANDROID_CREDS_H_ +#define ANDROID_CREDS_H_ + +#include <credentials/credential_set.h> + +typedef struct android_creds_t android_creds_t; + +/** + * Android credentials helper. + */ +struct android_creds_t { + + /** + * Implements credential_set_t + */ + credential_set_t set; + + /** + * Add a trusted CA certificate from the Android keystore to serve by + * this set. + * + * @param name name/ID of the certificate in the keystore + * @return FALSE if the certificate does not exist or is invalid + */ + bool (*add_certificate)(android_creds_t *this, char *name); + + /** + * Set the username and password for authentication. + * + * @param id ID of the user + * @param password password to use for authentication + */ + void (*set_username_password)(android_creds_t *this, identification_t *id, + char *password); + + /** + * Clear the stored credentials. + */ + void (*clear)(android_creds_t *this); + + /** + * Destroy a android_creds instance. + */ + void (*destroy)(android_creds_t *this); + +}; + +/** + * Create an android_creds instance. + */ +android_creds_t *android_creds_create(); + +#endif /** ANDROID_CREDS_H_ @}*/ diff --git a/src/libcharon/plugins/android/android_handler.c b/src/libcharon/plugins/android/android_handler.c index a475eeaab..ec3ff7a51 100644 --- a/src/libcharon/plugins/android/android_handler.c +++ b/src/libcharon/plugins/android/android_handler.c @@ -75,7 +75,7 @@ host_t *get_dns_server(int index) host_t *dns = NULL; char key[10], value[PROPERTY_VALUE_MAX]; - if (snprintf(key, sizeof(key), "net.dns%d", index) >= sizeof(key)) + if (snprintf(key, sizeof(key), "vpn.dns%d", index) >= sizeof(key)) { return NULL; } @@ -94,7 +94,7 @@ bool set_dns_server(int index, host_t *dns) { char key[10], value[PROPERTY_VALUE_MAX]; - if (snprintf(key, sizeof(key), "net.dns%d", index) >= sizeof(key)) + if (snprintf(key, sizeof(key), "vpn.dns%d", index) >= sizeof(key)) { return FALSE; } diff --git a/src/libcharon/plugins/android/android_logger.c b/src/libcharon/plugins/android/android_logger.c new file mode 100644 index 000000000..43c56e656 --- /dev/null +++ b/src/libcharon/plugins/android/android_logger.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2010 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <string.h> +#include <android/log.h> + +#include "android_logger.h" + +#include <library.h> +#include <daemon.h> + +typedef struct private_android_logger_t private_android_logger_t; + +/** + * Private data of an android_logger_t object + */ +struct private_android_logger_t { + + /** + * Public interface + */ + android_logger_t public; + + /** + * logging level + */ + int level; + +}; + + +METHOD(listener_t, log_, bool, + private_android_logger_t *this, debug_t group, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->level) + { + char sgroup[16], buffer[8192]; + char *current = buffer, *next; + snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group); + vsnprintf(buffer, sizeof(buffer), format, args); + while (current) + { /* log each line seperately */ + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + __android_log_print(ANDROID_LOG_INFO, "charon", "%.2d[%s] %s\n", + thread, sgroup, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +METHOD(android_logger_t, destroy, void, + private_android_logger_t *this) +{ + free(this); +} + +/** + * Described in header. + */ +android_logger_t *android_logger_create() +{ + private_android_logger_t *this; + + INIT(this, + .public = { + .listener = { + .log = _log_, + }, + .destroy = _destroy, + }, + .level = lib->settings->get_int(lib->settings, + "charon.plugins.android.loglevel", 1), + ); + + return &this->public; +} + diff --git a/src/libcharon/plugins/android/android_logger.h b/src/libcharon/plugins/android/android_logger.h new file mode 100644 index 000000000..c6fe5aff3 --- /dev/null +++ b/src/libcharon/plugins/android/android_logger.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup android_logger android_logger + * @{ @ingroup android + */ + +#ifndef ANDROID_LOGGER_H_ +#define ANDROID_LOGGER_H_ + +#include <bus/bus.h> + +typedef struct android_logger_t android_logger_t; + +/** + * Android specific logger. + */ +struct android_logger_t { + + /** + * Implements bus_listener_t interface + */ + listener_t listener; + + /** + * Destroy the logger. + */ + void (*destroy)(android_logger_t *this); + +}; + +/** + * Create an Android specific logger instance. + * + * @return logger instance + */ +android_logger_t *android_logger_create(); + +#endif /** ANDROID_LOGGER_H_ @}*/ diff --git a/src/libcharon/plugins/android/android_plugin.c b/src/libcharon/plugins/android/android_plugin.c index 9a558f53b..e2c8572ef 100644 --- a/src/libcharon/plugins/android/android_plugin.c +++ b/src/libcharon/plugins/android/android_plugin.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010 Tobias Brunner * Copyright (C) 2010 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -14,7 +15,10 @@ */ #include "android_plugin.h" +#include "android_logger.h" #include "android_handler.h" +#include "android_creds.h" +#include "android_service.h" #include <hydra.h> #include <daemon.h> @@ -32,16 +36,38 @@ struct private_android_plugin_t { android_plugin_t public; /** + * Android specific logger + */ + android_logger_t *logger; + + /** * Android specific DNS handler */ android_handler_t *handler; + + /** + * Android specific credential set + */ + android_creds_t *creds; + + /** + * Service that interacts with the Android Settings frontend + */ + android_service_t *service; + }; METHOD(plugin_t, destroy, void, - private_android_plugin_t *this) + private_android_plugin_t *this) { - hydra->attributes->remove_handler(hydra->attributes, &this->handler->handler); + hydra->attributes->remove_handler(hydra->attributes, + &this->handler->handler); + lib->credmgr->remove_set(lib->credmgr, &this->creds->set); + charon->bus->remove_listener(charon->bus, &this->logger->listener); + this->creds->destroy(this->creds); this->handler->destroy(this->handler); + this->logger->destroy(this->logger); + DESTROY_IF(this->service); free(this); } @@ -56,11 +82,22 @@ plugin_t *android_plugin_create() .public.plugin = { .destroy = _destroy, }, + .logger = android_logger_create(), .handler = android_handler_create(), + .creds = android_creds_create(), ); + charon->bus->add_listener(charon->bus, &this->logger->listener); + lib->credmgr->add_set(lib->credmgr, &this->creds->set); hydra->attributes->add_handler(hydra->attributes, &this->handler->handler); + this->service = android_service_create(this->creds); + if (!this->service) + { + destroy(this); + return NULL; + } + return &this->public.plugin; } diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c new file mode 100644 index 000000000..538c4a9a2 --- /dev/null +++ b/src/libcharon/plugins/android/android_service.c @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2010 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <unistd.h> +#include <cutils/sockets.h> +#include <cutils/properties.h> +#include <signal.h> + +#include "android_service.h" + +#include <daemon.h> +#include <threading/thread.h> +#include <processing/jobs/callback_job.h> + +typedef struct private_android_service_t private_android_service_t; + +/** + * private data of Android service + */ +struct private_android_service_t { + + /** + * public interface + */ + android_service_t public; + + /** + * current IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * job that handles requests from the Android control socket + */ + callback_job_t *job; + + /** + * android credentials + */ + android_creds_t *creds; + + /** + * android control socket + */ + int control; + +}; + +/** + * Some of the error codes defined in VpnManager.java + */ +typedef enum { + /** Error code to indicate an error from authentication. */ + VPN_ERROR_AUTH = 51, + /** Error code to indicate the connection attempt failed. */ + VPN_ERROR_CONNECTION_FAILED = 101, + /** Error code to indicate an error of remote server hanging up. */ + VPN_ERROR_REMOTE_HUNG_UP = 7, + /** Error code to indicate an error of losing connectivity. */ + VPN_ERROR_CONNECTION_LOST = 103, +} android_vpn_errors_t; + +/** + * send a status code back to the Android app + */ +static void send_status(private_android_service_t *this, u_char code) +{ + DBG1(DBG_CFG, "status of Android plugin changed: %d", code); + send(this->control, &code, 1, 0); +} + +METHOD(listener_t, ike_updown, bool, + private_android_service_t *this, ike_sa_t *ike_sa, bool up) +{ + /* this callback is only registered during initiation, so if the IKE_SA + * goes down we assume an authentication error */ + if (this->ike_sa == ike_sa && !up) + { + send_status(this, VPN_ERROR_AUTH); + return FALSE; + } + return TRUE; +} + +METHOD(listener_t, child_state_change, bool, + private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, + child_sa_state_t state) +{ + /* this callback is only registered during initiation, so we still have + * the control socket open */ + if (this->ike_sa == ike_sa && state == CHILD_DESTROYING) + { + send_status(this, VPN_ERROR_CONNECTION_FAILED); + return FALSE; + } + return TRUE; +} + +/** + * Callback used to shutdown the daemon + */ +static job_requeue_t shutdown_callback(void *data) +{ + kill(0, SIGTERM); + return JOB_REQUEUE_NONE; +} + +METHOD(listener_t, child_updown, bool, + private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, + bool up) +{ + if (this->ike_sa == ike_sa) + { + if (up) + { + /* disable the hooks registered to catch initiation failures */ + this->public.listener.ike_updown = NULL; + this->public.listener.child_state_change = NULL; + property_set("vpn.status", "ok"); + } + else + { + callback_job_t *job; + /* the control socket is closed as soon as vpn.status is set to "ok" + * and the daemon proxy then only checks for terminated daemons to + * detect lost connections, so... */ + DBG1(DBG_CFG, "connection lost, raising delayed SIGTERM"); + /* to avoid any conflicts we send the SIGTERM not directly from this + * callback, but from a different thread. we also delay it to avoid + * a race condition during a regular shutdown */ + job = callback_job_create(shutdown_callback, NULL, NULL, NULL); + charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, 1); + return FALSE; + } + } + return TRUE; +} + +METHOD(listener_t, ike_rekey, bool, + private_android_service_t *this, ike_sa_t *old, ike_sa_t *new) +{ + if (this->ike_sa == old) + { + this->ike_sa = new; + } + return TRUE; +} + +/** + * Read a string argument from the Android control socket + */ +static char *read_argument(int fd, u_char length) +{ + int offset = 0; + char *data = malloc(length + 1); + while (offset < length) + { + int n = recv(fd, &data[offset], length - offset, 0); + if (n < 0) + { + DBG1(DBG_CFG, "failed to read argument from Android" + " control socket: %s", strerror(errno)); + free(data); + return NULL; + } + offset += n; + } + data[length] = '\0'; + DBG3(DBG_CFG, "received argument from Android control socket: %s", data); + return data; +} + +/** + * handle the request received from the Android control socket + */ +static job_requeue_t initiate(private_android_service_t *this) +{ + bool oldstate; + int fd, i = 0; + char *hostname = NULL, *cacert = NULL, *username = NULL, *password = NULL; + identification_t *gateway = NULL, *user = NULL; + ike_cfg_t *ike_cfg; + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + traffic_selector_t *ts; + ike_sa_t *ike_sa; + auth_cfg_t *auth; + lifetime_cfg_t lifetime = { + .time = { + .life = 10800, /* 3h */ + .rekey = 10200, /* 2h50min */ + .jitter = 300 /* 5min */ + } + }; + + fd = accept(this->control, NULL, 0); + if (fd < 0) + { + DBG1(DBG_CFG, "accept on Android control socket failed: %s", + strerror(errno)); + return JOB_REQUEUE_NONE; + } + /* the original control socket is not used anymore */ + close(this->control); + this->control = fd; + + while (TRUE) + { + u_char length; + if (recv(fd, &length, 1, 0) != 1) + { + DBG1(DBG_CFG, "failed to read from Android control socket: %s", + strerror(errno)); + return JOB_REQUEUE_NONE; + } + + if (length == 0xFF) + { /* last argument */ + break; + } + else + { + switch (i++) + { + case 0: /* gateway */ + hostname = read_argument(fd, length); + break; + case 1: /* CA certificate name */ + cacert = read_argument(fd, length); + break; + case 2: /* username */ + username = read_argument(fd, length); + break; + case 3: /* password */ + password = read_argument(fd, length); + break; + } + } + } + + if (cacert) + { + if (!this->creds->add_certificate(this->creds, cacert)) + { + DBG1(DBG_CFG, "failed to load CA certificate"); + } + /* if this is a server cert we could use the cert subject as id + * but we have to test first if that possible to configure */ + } + + gateway = identification_create_from_string(hostname); + DBG1(DBG_CFG, "using CA certificate, gateway identitiy '%Y'", gateway); + + if (username) + { + user = identification_create_from_string(username); + this->creds->set_username_password(this->creds, user, password); + } + + ike_cfg = ike_cfg_create(TRUE, FALSE, "0.0.0.0", IKEV2_UDP_PORT, + hostname, IKEV2_UDP_PORT); + ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); + + peer_cfg = peer_cfg_create("android", 2, ike_cfg, CERT_SEND_IF_ASKED, + UNIQUE_REPLACE, 1, /* keyingtries */ + 36000, 0, /* rekey 10h, reauth none */ + 600, 600, /* jitter, over 10min */ + TRUE, 0, /* mobike, DPD */ + host_create_from_string("0.0.0.0", 0) /* virt */, + NULL, FALSE, NULL, NULL); /* pool, mediation */ + + auth = auth_cfg_create(); + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); + auth->add(auth, AUTH_RULE_IDENTITY, user); + peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE); + auth = auth_cfg_create(); + auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); + auth->add(auth, AUTH_RULE_IDENTITY, gateway); + peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE); + + child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL, + ACTION_NONE, ACTION_NONE, FALSE, 0, 0, + NULL, NULL); + child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); + ts = traffic_selector_create_dynamic(0, 0, 65535); + child_cfg->add_traffic_selector(child_cfg, TRUE, ts); + ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0", + 0, "255.255.255.255", 65535); + child_cfg->add_traffic_selector(child_cfg, FALSE, ts); + peer_cfg->add_child_cfg(peer_cfg, child_cfg); + /* get an additional reference because initiate consumes one */ + child_cfg->get_ref(child_cfg); + + /* get us an IKE_SA */ + ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager, + peer_cfg); + if (!ike_sa->get_peer_cfg(ike_sa)) + { + ike_sa->set_peer_cfg(ike_sa, peer_cfg); + } + peer_cfg->destroy(peer_cfg); + + /* store the IKE_SA so we can track its progress */ + this->ike_sa = ike_sa; + + /* confirm that we received the request */ + send_status(this, i); + + if (ike_sa->initiate(ike_sa, child_cfg, 0, NULL, NULL) != SUCCESS) + { + DBG1(DBG_CFG, "failed to initiate tunnel"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + send_status(this, VPN_ERROR_CONNECTION_FAILED); + return JOB_REQUEUE_NONE; + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return JOB_REQUEUE_NONE; +} + +METHOD(android_service_t, destroy, void, + private_android_service_t *this) +{ + charon->bus->remove_listener(charon->bus, &this->public.listener); + close(this->control); + free(this); +} + +/** + * See header + */ +android_service_t *android_service_create(android_creds_t *creds) +{ + private_android_service_t *this; + + INIT(this, + .public = { + .listener = { + .ike_updown = _ike_updown, + .child_state_change = _child_state_change, + .child_updown = _child_updown, + .ike_rekey = _ike_rekey, + }, + .destroy = _destroy, + }, + .creds = creds, + ); + + this->control = android_get_control_socket("charon"); + if (this->control == -1) + { + DBG1(DBG_CFG, "failed to get Android control socket"); + free(this); + return NULL; + } + + if (listen(this->control, 1) < 0) + { + DBG1(DBG_CFG, "failed to listen on Android control socket: %s", + strerror(errno)); + close(this->control); + free(this); + return NULL; + } + + charon->bus->add_listener(charon->bus, &this->public.listener); + this->job = callback_job_create((callback_job_cb_t)initiate, this, + NULL, NULL); + charon->processor->queue_job(charon->processor, (job_t*)this->job); + + return &this->public; +} + diff --git a/src/libcharon/plugins/android/android_service.h b/src/libcharon/plugins/android/android_service.h new file mode 100644 index 000000000..d096d6cd5 --- /dev/null +++ b/src/libcharon/plugins/android/android_service.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup android_service android_service + * @{ @ingroup android + */ + +#ifndef ANDROID_SERVICE_H_ +#define ANDROID_SERVICE_H_ + +typedef struct android_service_t android_service_t; + +#include <bus/listeners/listener.h> + +#include "android_creds.h" + +/** + * Service that interacts with the Android Settings frontend. + */ +struct android_service_t { + + /** + * Implements listener_t. + */ + listener_t listener; + + /** + * Destroy a android_service_t. + */ + void (*destroy)(android_service_t *this); + +}; + +/** + * Create an Android service instance. + * + * @param creds Android credentials + */ +android_service_t *android_service_create(android_creds_t *creds); + +#endif /** ANDROID_SERVICE_H_ @}*/ |