From 8430e54d83e71b8cc806e12274905b6d0ea10f0c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 12:52:05 +0200 Subject: Added an Android specific credential set that provides CA certificates via JNI --- .../jni/libandroidbridge/backend/android_creds.c | 153 +++++++++++++++++++++ .../jni/libandroidbridge/backend/android_creds.h | 57 ++++++++ 2 files changed, 210 insertions(+) create mode 100644 src/frontends/android/jni/libandroidbridge/backend/android_creds.c create mode 100644 src/frontends/android/jni/libandroidbridge/backend/android_creds.h (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c new file mode 100644 index 000000000..ee9549d9c --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2012 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 . + * + * 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 "android_creds.h" +#include "../charonservice.h" + +#include +#include +#include +#include + +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; + + /** + * Credential set storing trusted certificates + */ + mem_cred_t *creds; + + /** + * read/write lock to make sure certificates are only loaded once + */ + rwlock_t *lock; + + /** + * TRUE if certificates have been loaded via JNI + */ + bool loaded; +}; + +/** + * Load trusted certificates via charonservice (JNI). + */ +static void load_trusted_certificates(private_android_creds_t *this) +{ + linked_list_t *certs; + certificate_t *cert; + chunk_t *current; + + certs = charonservice->get_trusted_certificates(charonservice); + if (certs) + { + while (certs->remove_first(certs, (void**)¤t) == SUCCESS) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, *current, BUILD_END); + if (cert) + { + DBG2(DBG_CFG, "loaded CA certificate '%Y'", + cert->get_subject(cert)); + this->creds->add_cert(this->creds, TRUE, cert); + } + chunk_free(current); + free(current); + } + certs->destroy(certs); + } +} + +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) +{ + enumerator_t *enumerator; + + if (!trusted || (cert != CERT_ANY && cert != CERT_X509)) + { + return NULL; + } + this->lock->read_lock(this->lock); + if (!this->loaded) + { + this->lock->unlock(this->lock); + this->lock->write_lock(this->lock); + /* check again after acquiring the write lock */ + if (!this->loaded) + { + load_trusted_certificates(this); + this->loaded = TRUE; + } + this->lock->unlock(this->lock); + this->lock->read_lock(this->lock); + } + enumerator = this->creds->set.create_cert_enumerator(&this->creds->set, + cert, key, id, trusted); + return enumerator_create_cleaner(enumerator, (void*)this->lock->unlock, + this->lock); +} + +METHOD(android_creds_t, clear, void, + private_android_creds_t *this) +{ + this->lock->write_lock(this->lock); + this->creds->clear(this->creds); + this->loaded = FALSE; + this->lock->unlock(this->lock); +} + +METHOD(android_creds_t, destroy, void, + private_android_creds_t *this) +{ + clear(this); + this->creds->destroy(this->creds); + 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 = (void*)return_null, + .create_private_enumerator = (void*)return_null, + .create_cdp_enumerator = (void*)return_null, + .cache_cert = (void*)nop, + }, + .clear = _clear, + .destroy = _destroy, + }, + .creds = mem_cred_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.h b/src/frontends/android/jni/libandroidbridge/backend/android_creds.h new file mode 100644 index 000000000..4b19b1bf5 --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 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 . + * + * 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_backend + */ + +#ifndef ANDROID_CREDS_H_ +#define ANDROID_CREDS_H_ + +#include +#include + +typedef struct android_creds_t android_creds_t; + +/** + * Android credential set that provides CA certificates via JNI. + */ +struct android_creds_t { + + /** + * Implements credential_set_t + */ + credential_set_t set; + + /** + * Clear the cached CA certificates. + */ + 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_ @}*/ + -- cgit v1.2.3 From 3aa5c609c39f7a6418f9bbf67d46293a77e023fe Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 12:59:39 +0200 Subject: Android specific credential set also provides user credentials --- .../jni/libandroidbridge/backend/android_creds.c | 27 ++++++++++++++++++++-- .../jni/libandroidbridge/backend/android_creds.h | 14 +++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c index ee9549d9c..27023d721 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c @@ -34,7 +34,7 @@ struct private_android_creds_t { android_creds_t public; /** - * Credential set storing trusted certificates + * Credential set storing trusted certificates and user credentials */ mem_cred_t *creds; @@ -108,6 +108,28 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, this->lock); } +METHOD(android_creds_t, add_username_password, void, + private_android_creds_t *this, char *username, char *password) +{ + shared_key_t *shared_key; + identification_t *id; + chunk_t secret; + + secret = chunk_create(password, strlen(password)); + shared_key = shared_key_create(SHARED_EAP, chunk_clone(secret)); + id = identification_create_from_string(username); + + this->creds->add_shared(this->creds, shared_key, id, NULL); +} + +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) +{ + return this->creds->set.create_shared_enumerator(&this->creds->set, + type, me, other); +} + METHOD(android_creds_t, clear, void, private_android_creds_t *this) { @@ -137,11 +159,12 @@ android_creds_t *android_creds_create() .public = { .set = { .create_cert_enumerator = _create_cert_enumerator, - .create_shared_enumerator = (void*)return_null, + .create_shared_enumerator = _create_shared_enumerator, .create_private_enumerator = (void*)return_null, .create_cdp_enumerator = (void*)return_null, .cache_cert = (void*)nop, }, + .add_username_password = _add_username_password, .clear = _clear, .destroy = _destroy, }, diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.h b/src/frontends/android/jni/libandroidbridge/backend/android_creds.h index 4b19b1bf5..33de838c1 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_creds.h +++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.h @@ -27,7 +27,8 @@ typedef struct android_creds_t android_creds_t; /** - * Android credential set that provides CA certificates via JNI. + * Android credential set that provides CA certificates via JNI and supplied + * user credentials. */ struct android_creds_t { @@ -37,7 +38,16 @@ struct android_creds_t { credential_set_t set; /** - * Clear the cached CA certificates. + * Add user name and password for EAP authentication + * + * @param username user name + * @param password password + */ + void (*add_username_password)(android_creds_t *this, char *username, + char *password); + + /** + * Clear the cached certificates and stored credentials. */ void (*clear)(android_creds_t *this); -- cgit v1.2.3 From 66211196a70301dca7d271aab3a9c55b66a91e14 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 13:15:53 +0200 Subject: android_service_t handles initiation of an SA and tracks its progress Status updates are delivered via charonservice (JNI). --- .../jni/libandroidbridge/backend/android_service.c | 269 +++++++++++++++++++++ .../jni/libandroidbridge/backend/android_service.h | 61 +++++ 2 files changed, 330 insertions(+) create mode 100644 src/frontends/android/jni/libandroidbridge/backend/android_service.c create mode 100644 src/frontends/android/jni/libandroidbridge/backend/android_service.h (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c new file mode 100644 index 000000000..5c18924be --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2010-2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * 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 . + * + * 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 "android_service.h" +#include "../charonservice.h" + +#include +#include +#include + +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; + + /** + * local ipv4 address + */ + char *local_address; + + /** + * gateway + */ + char *gateway; + + /** + * username + */ + char *username; + +}; + +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.ike_state_change = NULL; + charonservice->update_status(charonservice, + CHARONSERVICE_CHILD_STATE_UP); + } + else + { + charonservice->update_status(charonservice, + CHARONSERVICE_CHILD_STATE_DOWN); + return FALSE; + } + } + return TRUE; +} + +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) + { + charonservice->update_status(charonservice, + CHARONSERVICE_AUTH_ERROR); + return FALSE; + } + return TRUE; +} + +METHOD(listener_t, ike_state_change, bool, + private_android_service_t *this, ike_sa_t *ike_sa, ike_sa_state_t state) +{ + /* this call back is only registered during initiation */ + if (this->ike_sa == ike_sa && state == IKE_DESTROYING) + { + charonservice->update_status(charonservice, + CHARONSERVICE_UNREACHABLE_ERROR); + return FALSE; + } + return TRUE; +} + +METHOD(listener_t, alert, bool, + private_android_service_t *this, ike_sa_t *ike_sa, alert_t alert, + va_list args) +{ + if (this->ike_sa == ike_sa) + { + switch (alert) + { + case ALERT_PEER_ADDR_FAILED: + charonservice->update_status(charonservice, + CHARONSERVICE_LOOKUP_ERROR); + break; + case ALERT_PEER_AUTH_FAILED: + charonservice->update_status(charonservice, + CHARONSERVICE_PEER_AUTH_ERROR); + break; + default: + break; + } + } + 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; +} + +static job_requeue_t initiate(private_android_service_t *this) +{ + identification_t *gateway, *user; + 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 */ + } + }; + + ike_cfg = ike_cfg_create(TRUE, TRUE, this->local_address, FALSE, + charon->socket->get_port(charon->socket, FALSE), + this->gateway, FALSE, IKEV2_UDP_PORT); + ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); + + peer_cfg = peer_cfg_create("android", IKEV2, ike_cfg, CERT_SEND_IF_ASKED, + UNIQUE_REPLACE, 1, /* keyingtries */ + 36000, 0, /* rekey 10h, reauth none */ + 600, 600, /* jitter, over 10min */ + TRUE, FALSE, /* mobike, aggressive */ + 0, 0, /* DPD delay, timeout */ + 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); + user = identification_create_from_string(this->username); + 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); + gateway = identification_create_from_string(this->gateway); + 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, ACTION_NONE, FALSE, + 0, 0, NULL, NULL, 0); + 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 us an IKE_SA */ + ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager, + peer_cfg); + if (!ike_sa) + { + peer_cfg->destroy(peer_cfg); + charonservice->update_status(charonservice, + CHARONSERVICE_GENERIC_ERROR); + return JOB_REQUEUE_NONE; + } + 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; + + /* get an additional reference because initiate consumes one */ + child_cfg->get_ref(child_cfg); + 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); + 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); + free(this->local_address); + free(this->username); + free(this->gateway); + free(this); +} + +/** + * See header + */ +android_service_t *android_service_create(char *local_address, char *gateway, + char *username) +{ + private_android_service_t *this; + + INIT(this, + .public = { + .listener = { + .ike_rekey = _ike_rekey, + .ike_updown = _ike_updown, + .ike_state_change = _ike_state_change, + .child_updown = _child_updown, + .alert = _alert, + }, + .destroy = _destroy, + }, + .local_address = local_address, + .username = username, + .gateway = gateway, + ); + + charon->bus->add_listener(charon->bus, &this->public.listener); + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create((callback_job_cb_t)initiate, this, + NULL, NULL)); + return &this->public; +} diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h new file mode 100644 index 000000000..a7bd8b059 --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * 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 . + * + * 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_backend + */ + +#ifndef ANDROID_SERVICE_H_ +#define ANDROID_SERVICE_H_ + +#include "android_creds.h" + +#include +#include + +typedef struct android_service_t android_service_t; + +/** + * Service that sets up an IKE_SA/CHILD_SA and handles events + */ +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. Queues a job that starts initiation of a + * new IKE SA. + * + * @param local_address local ip address + * @param gateway gateway address + * @param username user name (local identity) + */ +android_service_t *android_service_create(char *local_address, char *gateway, + char *username); + +#endif /** ANDROID_SERVICE_H_ @}*/ -- cgit v1.2.3 From 3a05756b423411a8dfba6c68adc4c85cbf1ec353 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:04:14 +0200 Subject: An Android specific attribute handler installs DNS servers via Builder --- .../jni/libandroidbridge/backend/android_attr.c | 120 +++++++++++++++++++++ .../jni/libandroidbridge/backend/android_attr.h | 52 +++++++++ 2 files changed, 172 insertions(+) create mode 100644 src/frontends/android/jni/libandroidbridge/backend/android_attr.c create mode 100644 src/frontends/android/jni/libandroidbridge/backend/android_attr.h (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_attr.c b/src/frontends/android/jni/libandroidbridge/backend/android_attr.c new file mode 100644 index 000000000..e8c506950 --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/backend/android_attr.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * 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 . + * + * 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 "android_attr.h" +#include "../charonservice.h" + +#include +#include +#include + +typedef struct private_android_attr_t private_android_attr_t; + +/** + * Private data of an android_attr_t object. + */ +struct private_android_attr_t { + + /** + * Public interface. + */ + android_attr_t public; +}; + +METHOD(attribute_handler_t, handle, bool, + private_android_attr_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + vpnservice_builder_t *builder; + host_t *dns; + + switch (type) + { + case INTERNAL_IP4_DNS: + dns = host_create_from_chunk(AF_INET, data, 0); + break; + default: + return FALSE; + } + + if (!dns || dns->is_anyaddr(dns)) + { + DESTROY_IF(dns); + return FALSE; + } + + builder = charonservice->get_vpnservice_builder(charonservice); + builder->add_dns(builder, dns); + dns->destroy(dns); + return TRUE; +} + +METHOD(attribute_handler_t, release, void, + private_android_attr_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + /* DNS servers cannot be removed from an existing TUN device */ +} + +METHOD(enumerator_t, enumerate_dns, bool, + enumerator_t *this, configuration_attribute_type_t *type, chunk_t *data) +{ + *type = INTERNAL_IP4_DNS; + *data = chunk_empty; + this->enumerate = (void*)return_false; + return TRUE; +} + +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_android_attr_t *this, identification_t *server, host_t *vip) +{ + enumerator_t *enumerator; + + INIT(enumerator, + .enumerate = (void*)_enumerate_dns, + .destroy = (void*)free, + ); + return enumerator; +} + +METHOD(android_attr_t, destroy, void, + private_android_attr_t *this) +{ + free(this); +} + +/** + * Described in header + */ +android_attr_t *android_attr_create() +{ + private_android_attr_t *this; + + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = _release, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .destroy = _destroy, + }, + ); + + return &this->public; +} + diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_attr.h b/src/frontends/android/jni/libandroidbridge/backend/android_attr.h new file mode 100644 index 000000000..56b02e1ce --- /dev/null +++ b/src/frontends/android/jni/libandroidbridge/backend/android_attr.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * 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 . + * + * 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_attr android_attr + * @{ @ingroup android_backend + */ + +#ifndef ANDROID_ATTR_H_ +#define ANDROID_ATTR_H_ + +#include +#include + +typedef struct android_attr_t android_attr_t; + +/** + * Handler for DNS configuration + */ +struct android_attr_t { + + /** + * implements the attribute_handler_t interface + */ + attribute_handler_t handler; + + /** + * Destroy a android_attr_t + */ + void (*destroy)(android_attr_t *this); +}; + +/** + * Create a android_attr_t instance. + */ +android_attr_t *android_attr_create(void); + +#endif /** ANDROID_ATTR_H_ @}*/ + -- cgit v1.2.3 From a2993d72435970805f271231bf169e9c5a092508 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:43:39 +0200 Subject: Create a TUN device via VpnService.Builder once the CHILD_SA is established --- .../jni/libandroidbridge/backend/android_service.c | 82 ++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 5c18924be..691d5fc6d 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -17,13 +17,17 @@ #include "android_service.h" #include "../charonservice.h" +#include "../vpnservice_builder.h" #include #include #include +#include typedef struct private_android_service_t private_android_service_t; +#define TUN_DEFAULT_MTU 1400 + /** * private data of Android service */ @@ -54,8 +58,72 @@ struct private_android_service_t { */ char *username; + /** + * lock to safely access the TUN device fd + */ + rwlock_t *lock; + + /** + * TUN device file descriptor + */ + int tunfd; + }; +/** + * Setup a new TUN device for the supplied SAs. + * Additional information such as DNS servers are gathered in appropriate + * listeners asynchronously. To be sure every required bit of information is + * available this should be called after the CHILD_SA has been established. + */ +static bool setup_tun_device(private_android_service_t *this, + ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + vpnservice_builder_t *builder; + int tunfd; + + DBG1(DBG_DMN, "setting up TUN device for CHILD_SA %s{%u}", + child_sa->get_name(child_sa), child_sa->get_reqid(child_sa)); + + builder = charonservice->get_vpnservice_builder(charonservice); + if (!builder->set_mtu(builder, TUN_DEFAULT_MTU)) + { + return FALSE; + } + + tunfd = builder->establish(builder); + if (tunfd == -1) + { + return FALSE; + } + + this->lock->write_lock(this->lock); + this->tunfd = tunfd; + this->lock->unlock(this->lock); + + DBG1(DBG_DMN, "successfully created TUN device"); + return TRUE; +} + +/** + * Close the current tun device + */ +static void close_tun_device(private_android_service_t *this) +{ + int tunfd; + + this->lock->write_lock(this->lock); + if (this->tunfd < 0) + { /* already closed (or never created) */ + this->lock->unlock(this->lock); + return; + } + tunfd = this->tunfd; + this->tunfd = -1; + this->lock->unlock(this->lock); + close(tunfd); +} + METHOD(listener_t, child_updown, bool, private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up) @@ -67,11 +135,20 @@ METHOD(listener_t, child_updown, bool, /* disable the hooks registered to catch initiation failures */ this->public.listener.ike_updown = NULL; this->public.listener.ike_state_change = NULL; + if (!setup_tun_device(this, ike_sa, child_sa)) + { + DBG1(DBG_DMN, "failed to setup TUN device"); + charonservice->update_status(charonservice, + CHARONSERVICE_GENERIC_ERROR); + return FALSE; + + } charonservice->update_status(charonservice, CHARONSERVICE_CHILD_STATE_UP); } else { + close_tun_device(this); charonservice->update_status(charonservice, CHARONSERVICE_CHILD_STATE_DOWN); return FALSE; @@ -230,6 +307,9 @@ METHOD(android_service_t, destroy, void, private_android_service_t *this) { charon->bus->remove_listener(charon->bus, &this->public.listener); + /* make sure the tun device is actually closed */ + close_tun_device(this); + this->lock->destroy(this->lock); free(this->local_address); free(this->username); free(this->gateway); @@ -255,9 +335,11 @@ android_service_t *android_service_create(char *local_address, char *gateway, }, .destroy = _destroy, }, + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .local_address = local_address, .username = username, .gateway = gateway, + .tunfd = -1, ); charon->bus->add_listener(charon->bus, &this->public.listener); -- cgit v1.2.3 From 62e6630b248b0f7e387dae8d16dd56db71262b35 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:46:22 +0200 Subject: Add virtual IP to the TUN device builder After the CHILD_SA is established we can easily get this address from the IKE_SA. --- .../android/jni/libandroidbridge/backend/android_service.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 691d5fc6d..d44bebc9a 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -80,13 +80,21 @@ static bool setup_tun_device(private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa) { vpnservice_builder_t *builder; + host_t *vip; int tunfd; DBG1(DBG_DMN, "setting up TUN device for CHILD_SA %s{%u}", child_sa->get_name(child_sa), child_sa->get_reqid(child_sa)); + vip = ike_sa->get_virtual_ip(ike_sa, TRUE); + if (!vip || vip->is_anyaddr(vip)) + { + DBG1(DBG_DMN, "setting up TUN device failed, no virtual IP found"); + return FALSE; + } builder = charonservice->get_vpnservice_builder(charonservice); - if (!builder->set_mtu(builder, TUN_DEFAULT_MTU)) + if (!builder->add_address(builder, vip) || + !builder->set_mtu(builder, TUN_DEFAULT_MTU)) { return FALSE; } -- cgit v1.2.3 From 30ba2ff7771e84a8ac7d08089945b1e6fa5cca38 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:47:47 +0200 Subject: Add routes based on the installed IPsec policies to the TUN device builder --- .../jni/libandroidbridge/backend/android_service.c | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index d44bebc9a..40ca86ac5 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -70,6 +70,51 @@ struct private_android_service_t { }; +/** + * Add a route to the TUN device builder + */ +static bool add_route(vpnservice_builder_t *builder, host_t *net, + u_int8_t prefix) +{ + /* if route is 0.0.0.0/0, split it into two routes 0.0.0.0/1 and + * 128.0.0.0/1 because otherwise it would conflict with the current default + * route */ + if (net->is_anyaddr(net) && prefix == 0) + { + bool success; + + success = add_route(builder, net, 1); + net = host_create_from_string("128.0.0.0", 0); + success = success && add_route(builder, net, 1); + net->destroy(net); + return success; + } + return builder->add_route(builder, net, prefix); +} + +/** + * Generate and set routes from installed IPsec policies + */ +static bool add_routes(vpnservice_builder_t *builder, child_sa_t *child_sa) +{ + traffic_selector_t *src_ts, *dst_ts; + enumerator_t *enumerator; + bool success = TRUE; + + enumerator = child_sa->create_policy_enumerator(child_sa); + while (success && enumerator->enumerate(enumerator, &src_ts, &dst_ts)) + { + host_t *net; + u_int8_t prefix; + + dst_ts->to_subnet(dst_ts, &net, &prefix); + success = add_route(builder, net, prefix); + net->destroy(net); + } + enumerator->destroy(enumerator); + return success; +} + /** * Setup a new TUN device for the supplied SAs. * Additional information such as DNS servers are gathered in appropriate @@ -94,6 +139,7 @@ static bool setup_tun_device(private_android_service_t *this, builder = charonservice->get_vpnservice_builder(charonservice); if (!builder->add_address(builder, vip) || + !add_routes(builder, child_sa) || !builder->set_mtu(builder, TUN_DEFAULT_MTU)) { return FALSE; -- cgit v1.2.3 From 3b3cf0c87ae5a73fad48933ff070f4b8ec919d98 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:49:52 +0200 Subject: Add simple callbacks to receive/send ESP packets via libipsec/receiver. --- .../jni/libandroidbridge/backend/android_service.c | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 40ca86ac5..bd28e7de9 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -70,6 +71,25 @@ struct private_android_service_t { }; +/** + * Outbound callback + */ +static void send_esp(void *data, esp_packet_t *packet) +{ + charon->sender->send_no_marker(charon->sender, (packet_t*)packet); +} + +/** + * Receiver callback + */ +static void receiver_esp_cb(void *data, packet_t *packet) +{ + esp_packet_t *esp_packet; + + esp_packet = esp_packet_create_from_packet(packet); + ipsec->processor->queue_inbound(ipsec->processor, esp_packet); +} + /** * Add a route to the TUN device builder */ @@ -156,6 +176,12 @@ static bool setup_tun_device(private_android_service_t *this, this->lock->unlock(this->lock); DBG1(DBG_DMN, "successfully created TUN device"); + + charon->receiver->add_esp_cb(charon->receiver, + (receiver_esp_cb_t)receiver_esp_cb, NULL); + ipsec->processor->register_outbound(ipsec->processor, + (ipsec_outbound_cb_t)send_esp, NULL); + return TRUE; } @@ -175,6 +201,11 @@ static void close_tun_device(private_android_service_t *this) tunfd = this->tunfd; this->tunfd = -1; this->lock->unlock(this->lock); + + ipsec->processor->unregister_outbound(ipsec->processor, + (ipsec_outbound_cb_t)send_esp); + charon->receiver->del_esp_cb(charon->receiver, + (receiver_esp_cb_t)receiver_esp_cb); close(tunfd); } -- cgit v1.2.3 From d9531100fac082a803b2d2d4a7505a638ab1d3dd Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:51:59 +0200 Subject: Added a handler that writes inbound plain text packets to the TUN device --- .../jni/libandroidbridge/backend/android_service.c | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index bd28e7de9..417a8aa70 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -15,6 +15,8 @@ * for more details. */ +#include + #include "android_service.h" #include "../charonservice.h" #include "../vpnservice_builder.h" @@ -79,6 +81,35 @@ static void send_esp(void *data, esp_packet_t *packet) charon->sender->send_no_marker(charon->sender, (packet_t*)packet); } +/** + * Inbound callback + */ +static void deliver_plain(private_android_service_t *this, + ip_packet_t *packet) +{ + chunk_t encoding; + ssize_t len; + + encoding = packet->get_encoding(packet); + + this->lock->read_lock(this->lock); + if (this->tunfd < 0) + { /* the TUN device is already closed */ + this->lock->unlock(this->lock); + packet->destroy(packet); + return; + } + len = write(this->tunfd, encoding.ptr, encoding.len); + this->lock->unlock(this->lock); + + if (len < 0 || len != encoding.len) + { + DBG1(DBG_DMN, "failed to write packet to TUN device: %s", + strerror(errno)); + } + packet->destroy(packet); +} + /** * Receiver callback */ @@ -179,6 +210,8 @@ static bool setup_tun_device(private_android_service_t *this, charon->receiver->add_esp_cb(charon->receiver, (receiver_esp_cb_t)receiver_esp_cb, NULL); + ipsec->processor->register_inbound(ipsec->processor, + (ipsec_inbound_cb_t)deliver_plain, this); ipsec->processor->register_outbound(ipsec->processor, (ipsec_outbound_cb_t)send_esp, NULL); @@ -204,6 +237,8 @@ static void close_tun_device(private_android_service_t *this) ipsec->processor->unregister_outbound(ipsec->processor, (ipsec_outbound_cb_t)send_esp); + ipsec->processor->unregister_inbound(ipsec->processor, + (ipsec_inbound_cb_t)deliver_plain); charon->receiver->del_esp_cb(charon->receiver, (receiver_esp_cb_t)receiver_esp_cb); close(tunfd); -- cgit v1.2.3 From 2483f6a4e0eee7ea507c5dbbad0c2cdff49e13d6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2012 14:54:44 +0200 Subject: Job added which handles plain text packets read from TUN device --- .../jni/libandroidbridge/backend/android_service.c | 64 +++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) (limited to 'src/frontends/android/jni/libandroidbridge/backend') diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 417a8aa70..dfc0d2342 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -15,6 +15,7 @@ * for more details. */ +#include #include #include "android_service.h" @@ -26,6 +27,7 @@ #include #include #include +#include typedef struct private_android_service_t private_android_service_t; @@ -121,6 +123,62 @@ static void receiver_esp_cb(void *data, packet_t *packet) ipsec->processor->queue_inbound(ipsec->processor, esp_packet); } +/** + * Job handling outbound plaintext packets + */ +static job_requeue_t handle_plain(private_android_service_t *this) +{ + ip_packet_t *packet; + chunk_t raw; + fd_set set; + ssize_t len; + int tunfd; + bool old; + + FD_ZERO(&set); + + this->lock->read_lock(this->lock); + if (this->tunfd < 0) + { /* the TUN device is already closed */ + this->lock->unlock(this->lock); + return JOB_REQUEUE_NONE; + } + tunfd = this->tunfd; + FD_SET(tunfd, &set); + this->lock->unlock(this->lock); + + old = thread_cancelability(TRUE); + len = select(tunfd + 1, &set, NULL, NULL, NULL); + thread_cancelability(old); + + if (len < 0) + { + DBG1(DBG_DMN, "select on TUN device failed: %s", strerror(errno)); + return JOB_REQUEUE_NONE; + } + + raw = chunk_alloc(TUN_DEFAULT_MTU); + len = read(tunfd, raw.ptr, raw.len); + if (len < 0) + { + DBG1(DBG_DMN, "reading from TUN device failed: %s", strerror(errno)); + chunk_free(&raw); + return JOB_REQUEUE_FAIR; + } + raw.len = len; + + packet = ip_packet_create(raw); + if (packet) + { + ipsec->processor->queue_outbound(ipsec->processor, packet); + } + else + { + DBG1(DBG_DMN, "invalid IP packet read from TUN device"); + } + return JOB_REQUEUE_DIRECT; +} + /** * Add a route to the TUN device builder */ @@ -167,7 +225,8 @@ static bool add_routes(vpnservice_builder_t *builder, child_sa_t *child_sa) } /** - * Setup a new TUN device for the supplied SAs. + * Setup a new TUN device for the supplied SAs, also queues a job that + * reads packets from this device. * Additional information such as DNS servers are gathered in appropriate * listeners asynchronously. To be sure every required bit of information is * available this should be called after the CHILD_SA has been established. @@ -215,6 +274,9 @@ static bool setup_tun_device(private_android_service_t *this, ipsec->processor->register_outbound(ipsec->processor, (ipsec_outbound_cb_t)send_esp, NULL); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create((callback_job_cb_t)handle_plain, this, + NULL, (callback_job_cancel_t)return_false)); return TRUE; } -- cgit v1.2.3