diff options
author | Tobias Brunner <tobias@strongswan.org> | 2014-07-22 11:10:59 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2014-07-22 11:14:00 +0200 |
commit | 1ddc1ec0b37355be22d55728557b88cde83292e6 (patch) | |
tree | 0c2eb284b44c121ca1b49d3d30dc71f9f42bf412 /src/frontends/android/jni/libandroidbridge/backend/android_service.c | |
parent | 32109a535f3f0ae3e234ebfefc7c69dfc2327c67 (diff) | |
parent | ffff7219ef6af21c9497af8db49bfb3c1c9a3036 (diff) | |
download | strongswan-1ddc1ec0b37355be22d55728557b88cde83292e6.tar.bz2 strongswan-1ddc1ec0b37355be22d55728557b88cde83292e6.tar.xz |
Merge branch 'android-dns-proxy'
Adds a DNS proxy feature that uses VPN-protected sockets to resolve the
VPN gateway's hostname while reestablishing the IKE_SA, which is
required because we keep the TUN device up to avoid leaking plaintext
traffic.
The TUN device is recreated without DNS servers before reestablishing in
case the VPN server pushed DNS servers to the client that are only
reachable via VPN.
Fixes #622.
Diffstat (limited to 'src/frontends/android/jni/libandroidbridge/backend/android_service.c')
-rw-r--r-- | src/frontends/android/jni/libandroidbridge/backend/android_service.c | 134 |
1 files changed, 127 insertions, 7 deletions
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index d73dc4582..e60c491c1 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 Tobias Brunner + * Copyright (C) 2010-2014 Tobias Brunner * Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Ralf Sager * Hochschule fuer Technik Rapperswil @@ -19,6 +19,7 @@ #include <unistd.h> #include "android_service.h" +#include "android_dns_proxy.h" #include "../charonservice.h" #include "../vpnservice_builder.h" @@ -83,6 +84,15 @@ struct private_android_service_t { */ int tunfd; + /** + * DNS proxy + */ + android_dns_proxy_t *dns_proxy; + + /** + * Whether to use the DNS proxy or not + */ + bool use_dns_proxy; }; /** @@ -143,7 +153,7 @@ static job_requeue_t handle_plain(private_android_service_t *this) fd_set set; ssize_t len; int tunfd; - bool old; + bool old, dns_proxy; timeval_t tv = { /* check every second if tunfd is still valid */ .tv_sec = 1, @@ -159,6 +169,8 @@ static job_requeue_t handle_plain(private_android_service_t *this) } tunfd = this->tunfd; FD_SET(tunfd, &set); + /* cache this while we have the lock */ + dns_proxy = this->use_dns_proxy; this->lock->unlock(this->lock); old = thread_cancelability(TRUE); @@ -192,7 +204,10 @@ static job_requeue_t handle_plain(private_android_service_t *this) packet = ip_packet_create(raw); if (packet) { - ipsec->processor->queue_outbound(ipsec->processor, packet); + if (!dns_proxy || !this->dns_proxy->handle(this->dns_proxy, packet)) + { + ipsec->processor->queue_outbound(ipsec->processor, packet); + } } else { @@ -324,6 +339,8 @@ static bool setup_tun_device(private_android_service_t *this, (ipsec_inbound_cb_t)deliver_plain, this); ipsec->processor->register_outbound(ipsec->processor, (ipsec_outbound_cb_t)send_esp, NULL); + this->dns_proxy->register_cb(this->dns_proxy, + (dns_proxy_response_cb_t)deliver_plain, this); lib->processor->queue_job(lib->processor, (job_t*)callback_job_create((callback_job_cb_t)handle_plain, this, @@ -333,6 +350,36 @@ static bool setup_tun_device(private_android_service_t *this, } /** + * Setup a new TUN device based on the existing one, but without DNS server. + */ +static bool setup_tun_device_without_dns(private_android_service_t *this) +{ + vpnservice_builder_t *builder; + int tunfd; + + DBG1(DBG_DMN, "setting up TUN device without DNS"); + + builder = charonservice->get_vpnservice_builder(charonservice); + + tunfd = builder->establish_no_dns(builder); + if (tunfd == -1) + { + return FALSE; + } + + this->lock->write_lock(this->lock); + if (this->tunfd > 0) + { /* close previously opened TUN device, this should always be the case */ + close(this->tunfd); + } + this->tunfd = tunfd; + this->lock->unlock(this->lock); + + DBG1(DBG_DMN, "successfully created TUN device without DNS"); + return TRUE; +} + +/** * Close the current tun device */ static void close_tun_device(private_android_service_t *this) @@ -349,6 +396,8 @@ static void close_tun_device(private_android_service_t *this) this->tunfd = -1; this->lock->unlock(this->lock); + this->dns_proxy->unregister_cb(this->dns_proxy, + (dns_proxy_response_cb_t)deliver_plain); ipsec->processor->unregister_outbound(ipsec->processor, (ipsec_outbound_cb_t)send_esp); ipsec->processor->unregister_inbound(ipsec->processor, @@ -358,6 +407,17 @@ static void close_tun_device(private_android_service_t *this) close(tunfd); } +/** + * Terminate the IKE_SA with the given unique ID + */ +CALLBACK(terminate, job_requeue_t, + u_int32_t *id) +{ + charon->controller->terminate_ike(charon->controller, *id, + controller_cb_empty, NULL, 0); + 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) @@ -368,6 +428,11 @@ METHOD(listener_t, child_updown, bool, { /* disable the hooks registered to catch initiation failures */ this->public.listener.ike_updown = NULL; + /* CHILD_SA is up so we can disable the DNS proxy we enabled to + * reestablish the SA */ + this->lock->write_lock(this->lock); + this->use_dns_proxy = FALSE; + this->lock->unlock(this->lock); if (!setup_tun_device(this, ike_sa, child_sa)) { DBG1(DBG_DMN, "failed to setup TUN device"); @@ -422,9 +487,37 @@ METHOD(listener_t, alert, bool, case ALERT_PEER_INIT_UNREACHABLE: this->lock->read_lock(this->lock); if (this->tunfd < 0) - { /* only handle this if we are not reestablishing the SA */ + { + u_int32_t *id = malloc_thing(u_int32_t); + + /* always fail if we are not able to initiate the IKE_SA + * initially */ charonservice->update_status(charonservice, CHARONSERVICE_UNREACHABLE_ERROR); + /* terminate the IKE_SA so no further keying tries are + * attempted */ + *id = ike_sa->get_unique_id(ike_sa); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)terminate, id, free, + (callback_job_cancel_t)return_false, JOB_PRIO_HIGH)); + } + else + { + peer_cfg_t *peer_cfg; + u_int32_t tries, try; + + /* when reestablishing and if keyingtries is not %forever + * the IKE_SA is destroyed after the set number of tries, + * so notify the GUI */ + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + tries = peer_cfg->get_keyingtries(peer_cfg); + try = va_arg(args, u_int32_t); + if (tries != 0 && try == tries-1) + { + charonservice->update_status(charonservice, + CHARONSERVICE_UNREACHABLE_ERROR); + } } this->lock->unlock(this->lock); break; @@ -445,11 +538,34 @@ METHOD(listener_t, ike_rekey, bool, return TRUE; } -METHOD(listener_t, ike_reestablish, bool, +METHOD(listener_t, ike_reestablish_pre, bool, private_android_service_t *this, ike_sa_t *old, ike_sa_t *new) { if (this->ike_sa == old) { + /* enable DNS proxy so hosts are properly resolved while the TUN device + * is still active */ + this->lock->write_lock(this->lock); + this->use_dns_proxy = TRUE; + this->lock->unlock(this->lock); + /* if DNS servers are installed that are only reachable through the VPN + * the DNS proxy doesn't help, so uninstall DNS servers */ + if (!setup_tun_device_without_dns(this)) + { + DBG1(DBG_DMN, "failed to setup TUN device without DNS"); + charonservice->update_status(charonservice, + CHARONSERVICE_GENERIC_ERROR); + } + } + return TRUE; +} + +METHOD(listener_t, ike_reestablish_post, bool, + private_android_service_t *this, ike_sa_t *old, ike_sa_t *new, + bool initiated) +{ + if (this->ike_sa == old && initiated) + { this->ike_sa = new; /* re-register hook to detect initiation failures */ this->public.listener.ike_updown = _ike_updown; @@ -458,7 +574,6 @@ METHOD(listener_t, ike_reestablish, bool, * get ignored and thus we trigger the event here */ charonservice->update_status(charonservice, CHARONSERVICE_CHILD_STATE_DOWN); - /* the TUN device will be closed when the new CHILD_SA is established */ } return TRUE; } @@ -630,6 +745,7 @@ METHOD(android_service_t, destroy, void, charon->bus->remove_listener(charon->bus, &this->public.listener); /* make sure the tun device is actually closed */ close_tun_device(this); + this->dns_proxy->destroy(this->dns_proxy); this->lock->destroy(this->lock); free(this->type); free(this->gateway); @@ -655,7 +771,8 @@ android_service_t *android_service_create(android_creds_t *creds, char *type, .public = { .listener = { .ike_rekey = _ike_rekey, - .ike_reestablish = _ike_reestablish, + .ike_reestablish_pre = _ike_reestablish_pre, + .ike_reestablish_post = _ike_reestablish_post, .ike_updown = _ike_updown, .child_updown = _child_updown, .alert = _alert, @@ -663,6 +780,7 @@ android_service_t *android_service_create(android_creds_t *creds, char *type, .destroy = _destroy, }, .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .dns_proxy = android_dns_proxy_create(), .username = username, .password = password, .gateway = gateway, @@ -670,6 +788,8 @@ android_service_t *android_service_create(android_creds_t *creds, char *type, .type = type, .tunfd = -1, ); + /* only allow queries for the VPN gateway */ + this->dns_proxy->add_hostname(this->dns_proxy, gateway); charon->bus->add_listener(charon->bus, &this->public.listener); |