Merge branch 'android-app'
This branch introduces a userland IPsec implementation (libipsec) and an Android App which targets the VpnService API that is provided by Android 4+. The implementation is based on the bachelor thesis 'Userland IPsec for Android 4' by Giuliano Grassi and Ralf Sager.
This commit is contained in:
commit
09ae3d79ca
|
@ -432,6 +432,7 @@ AC_CHECK_FUNCS(prctl mallinfo getpass closefrom getpwnam_r getgrnam_r)
|
|||
|
||||
AC_CHECK_HEADERS(sys/sockio.h glob.h)
|
||||
AC_CHECK_HEADERS(net/pfkeyv2.h netipsec/ipsec.h netinet6/ipsec.h linux/udp.h)
|
||||
AC_CHECK_HEADERS(netinet/ip6.h)
|
||||
|
||||
AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [],
|
||||
[
|
||||
|
|
|
@ -1,29 +1,71 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.strongswan.android"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
android:versionName="1.0.0" >
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name" >
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/ApplicationTheme" >
|
||||
<activity
|
||||
android:name=".strongSwanActivity"
|
||||
android:label="@string/app_name" >
|
||||
android:name=".ui.MainActivity"
|
||||
android:label="@string/main_activity_name"
|
||||
android:launchMode="singleTop" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".CharonVpnService" android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
<activity
|
||||
android:name=".ui.VpnProfileDetailActivity" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.LogActivity"
|
||||
android:label="@string/log_title" >
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".logic.VpnStateService"
|
||||
android:exported="false" >
|
||||
</service>
|
||||
<service
|
||||
android:name=".logic.CharonVpnService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE" >
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
<action android:name="org.strongswan.android.logic.CharonVpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name=".data.LogContentProvider"
|
||||
android:authorities="org.strongswan.android.content.log" >
|
||||
<!-- android:grantUriPermissions="true" combined with a custom permission does
|
||||
not work (probably too many indirections with ACTION_SEND) so we secure
|
||||
this provider with a custom ticketing system -->
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
@ -3,7 +3,14 @@ include $(CLEAR_VARS)
|
|||
|
||||
# copy-n-paste from Makefile.am
|
||||
LOCAL_SRC_FILES := \
|
||||
charonservice.c
|
||||
android_jni.c android_jni.h \
|
||||
backend/android_attr.c backend/android_attr.h \
|
||||
backend/android_creds.c backend/android_creds.h \
|
||||
backend/android_service.c backend/android_service.h \
|
||||
charonservice.c charonservice.h \
|
||||
kernel/android_ipsec.c kernel/android_ipsec.h \
|
||||
kernel/android_net.c kernel/android_net.h \
|
||||
vpnservice_builder.c vpnservice_builder.h
|
||||
|
||||
# build libandroidbridge -------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 <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 "android_jni.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <threading/thread_value.h>
|
||||
|
||||
/**
|
||||
* JVM
|
||||
*/
|
||||
static JavaVM *android_jvm;
|
||||
|
||||
jclass *android_charonvpnservice_class;
|
||||
jclass *android_charonvpnservice_builder_class;
|
||||
|
||||
/**
|
||||
* Thread-local variable. Only used because of the destructor
|
||||
*/
|
||||
static thread_value_t *androidjni_threadlocal;
|
||||
|
||||
/**
|
||||
* Thread-local destructor to ensure that a native thread is detached
|
||||
* from the JVM even if androidjni_detach_thread() is not called.
|
||||
*/
|
||||
static void attached_thread_cleanup(void *arg)
|
||||
{
|
||||
(*android_jvm)->DetachCurrentThread(android_jvm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void androidjni_attach_thread(JNIEnv **env)
|
||||
{
|
||||
if ((*android_jvm)->GetEnv(android_jvm, (void**)env,
|
||||
JNI_VERSION_1_6) == JNI_OK)
|
||||
{ /* already attached or even a Java thread */
|
||||
return;
|
||||
}
|
||||
(*android_jvm)->AttachCurrentThread(android_jvm, env, NULL);
|
||||
/* use a thread-local value with a destructor that automatically detaches
|
||||
* the thread from the JVM before it terminates, if not done manually */
|
||||
androidjni_threadlocal->set(androidjni_threadlocal, (void*)*env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
void androidjni_detach_thread()
|
||||
{
|
||||
if (androidjni_threadlocal->get(androidjni_threadlocal))
|
||||
{ /* only do this if we actually attached this thread */
|
||||
androidjni_threadlocal->set(androidjni_threadlocal, NULL);
|
||||
(*android_jvm)->DetachCurrentThread(android_jvm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this library is loaded by the JVM
|
||||
*/
|
||||
jint JNI_OnLoad(JavaVM *vm, void *reserved)
|
||||
{
|
||||
JNIEnv *env;
|
||||
|
||||
android_jvm = vm;
|
||||
|
||||
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
androidjni_threadlocal = thread_value_create(attached_thread_cleanup);
|
||||
|
||||
android_charonvpnservice_class =
|
||||
(*env)->NewGlobalRef(env, (*env)->FindClass(env,
|
||||
JNI_PACKAGE_STRING "/CharonVpnService"));
|
||||
android_charonvpnservice_builder_class =
|
||||
(*env)->NewGlobalRef(env, (*env)->FindClass(env,
|
||||
JNI_PACKAGE_STRING "/CharonVpnService$BuilderAdapter"));
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this library is unloaded by the JVM (which never happens on
|
||||
* Android)
|
||||
*/
|
||||
void JNI_OnUnload(JavaVM *vm, void *reserved)
|
||||
{
|
||||
androidjni_threadlocal->destroy(androidjni_threadlocal);
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 <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_jni android_jni
|
||||
* @{ @ingroup libandroidbridge
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_JNI_H_
|
||||
#define ANDROID_JNI_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <library.h>
|
||||
|
||||
#define JNI_PACKAGE org_strongswan_android_logic
|
||||
#define JNI_PACKAGE_STRING "org/strongswan/android/logic"
|
||||
|
||||
#define JNI_METHOD_PP(pack, klass, name, ret, ...) \
|
||||
ret Java_##pack##_##klass##_##name(JNIEnv *env, jobject this, ##__VA_ARGS__)
|
||||
|
||||
#define JNI_METHOD_P(pack, klass, name, ret, ...) \
|
||||
JNI_METHOD_PP(pack, klass, name, ret, ##__VA_ARGS__)
|
||||
|
||||
#define JNI_METHOD(klass, name, ret, ...) \
|
||||
JNI_METHOD_P(JNI_PACKAGE, klass, name, ret, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Java classes
|
||||
* Initialized in JNI_OnLoad()
|
||||
*/
|
||||
extern jclass *android_charonvpnservice_class;
|
||||
extern jclass *android_charonvpnservice_builder_class;
|
||||
|
||||
/**
|
||||
* Attach the current thread to the JVM
|
||||
*
|
||||
* As local JNI references are not freed until the thread detaches
|
||||
* androidjni_detach_thread() should be called as soon as possible.
|
||||
* If it is not called a thread-local destructor ensures that the
|
||||
* thread is at least detached as soon as it terminates.
|
||||
*
|
||||
* @param env JNIEnv
|
||||
*/
|
||||
void androidjni_attach_thread(JNIEnv **env);
|
||||
|
||||
/**
|
||||
* Detach the current thread from the JVM
|
||||
*
|
||||
* Call this as soon as possible to ensure that local JNI references are freed.
|
||||
*/
|
||||
void androidjni_detach_thread();
|
||||
|
||||
/**
|
||||
* Handle exceptions thrown by a JNI call
|
||||
*
|
||||
* @param env JNIEnv
|
||||
* @return TRUE if an exception was thrown
|
||||
*/
|
||||
static inline bool androidjni_exception_occurred(JNIEnv *env)
|
||||
{
|
||||
if ((*env)->ExceptionOccurred(env))
|
||||
{ /* clear any exception, otherwise the VM is terminated */
|
||||
(*env)->ExceptionDescribe(env);
|
||||
(*env)->ExceptionClear(env);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Java string to a C string. Memory is allocated.
|
||||
*
|
||||
* @param env JNIEnv
|
||||
* @param jstr Java string
|
||||
* @return native C string (allocated)
|
||||
*/
|
||||
static inline char *androidjni_convert_jstring(JNIEnv *env, jstring jstr)
|
||||
{
|
||||
char *str;
|
||||
jsize len;
|
||||
|
||||
len = (*env)->GetStringUTFLength(env, jstr);
|
||||
str = malloc(len + 1);
|
||||
(*env)->GetStringUTFRegion(env, jstr, 0, len, str);
|
||||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif /** ANDROID_JNI_H_ @}*/
|
|
@ -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 <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 "android_attr.h"
|
||||
#include "../charonservice.h"
|
||||
|
||||
#include <hydra.h>
|
||||
#include <debug.h>
|
||||
#include <library.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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 <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_attr android_attr
|
||||
* @{ @ingroup android_backend
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_ATTR_H_
|
||||
#define ANDROID_ATTR_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <attributes/attribute_handler.h>
|
||||
|
||||
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_ @}*/
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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 <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 "android_creds.h"
|
||||
#include "../charonservice.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include <library.h>
|
||||
#include <credentials/sets/mem_cred.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;
|
||||
|
||||
/**
|
||||
* Credential set storing trusted certificates and user credentials
|
||||
*/
|
||||
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, 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)
|
||||
{
|
||||
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 = _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,
|
||||
},
|
||||
.creds = mem_cred_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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 <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_backend
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_CREDS_H_
|
||||
#define ANDROID_CREDS_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <credentials/credential_set.h>
|
||||
|
||||
typedef struct android_creds_t android_creds_t;
|
||||
|
||||
/**
|
||||
* Android credential set that provides CA certificates via JNI and supplied
|
||||
* user credentials.
|
||||
*/
|
||||
struct android_creds_t {
|
||||
|
||||
/**
|
||||
* Implements credential_set_t
|
||||
*/
|
||||
credential_set_t set;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
|
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* 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 <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 <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "android_service.h"
|
||||
#include "../charonservice.h"
|
||||
#include "../vpnservice_builder.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include <library.h>
|
||||
#include <ipsec.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <threading/thread.h>
|
||||
|
||||
typedef struct private_android_service_t private_android_service_t;
|
||||
|
||||
#define TUN_DEFAULT_MTU 1400
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* lock to safely access the TUN device fd
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
|
||||
/**
|
||||
* TUN device file descriptor
|
||||
*/
|
||||
int tunfd;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Outbound callback
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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, 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.
|
||||
*/
|
||||
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->add_address(builder, vip) ||
|
||||
!add_routes(builder, child_sa) ||
|
||||
!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");
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
/* 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);
|
||||
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,
|
||||
},
|
||||
.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);
|
||||
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create((callback_job_cb_t)initiate, this,
|
||||
NULL, NULL));
|
||||
return &this->public;
|
||||
}
|
|
@ -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 <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_backend
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_SERVICE_H_
|
||||
#define ANDROID_SERVICE_H_
|
||||
|
||||
#include "android_creds.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <bus/listeners/listener.h>
|
||||
|
||||
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_ @}*/
|
|
@ -1,4 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
|
@ -13,30 +15,78 @@
|
|||
* for more details.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "charonservice.h"
|
||||
#include "android_jni.h"
|
||||
#include "backend/android_attr.h"
|
||||
#include "backend/android_creds.h"
|
||||
#include "backend/android_service.h"
|
||||
#include "kernel/android_ipsec.h"
|
||||
#include "kernel/android_net.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include <hydra.h>
|
||||
#include <ipsec.h>
|
||||
#include <daemon.h>
|
||||
#include <library.h>
|
||||
#include <threading/thread.h>
|
||||
|
||||
#define JNI_PACKAGE org_strongswan_android
|
||||
#define ANDROID_DEBUG_LEVEL 1
|
||||
#define ANDROID_RETRASNMIT_TRIES 3
|
||||
#define ANDROID_RETRANSMIT_TIMEOUT 3.0
|
||||
#define ANDROID_RETRANSMIT_BASE 1.4
|
||||
|
||||
#define JNI_METHOD_PP(pack, klass, name, ret, ...) \
|
||||
ret Java_##pack##_##klass##_##name(JNIEnv *env, jobject this, ##__VA_ARGS__)
|
||||
typedef struct private_charonservice_t private_charonservice_t;
|
||||
|
||||
#define JNI_METHOD_P(pack, klass, name, ret, ...) \
|
||||
JNI_METHOD_PP(pack, klass, name, ret, ##__VA_ARGS__)
|
||||
/**
|
||||
* private data of charonservice
|
||||
*/
|
||||
struct private_charonservice_t {
|
||||
|
||||
#define JNI_METHOD(klass, name, ret, ...) \
|
||||
JNI_METHOD_P(JNI_PACKAGE, klass, name, ret, ##__VA_ARGS__)
|
||||
/**
|
||||
* public interface
|
||||
*/
|
||||
charonservice_t public;
|
||||
|
||||
/**
|
||||
* android_attr instance
|
||||
*/
|
||||
android_attr_t *attr;
|
||||
|
||||
/**
|
||||
* android_creds instance
|
||||
*/
|
||||
android_creds_t *creds;
|
||||
|
||||
/**
|
||||
* android_service instance
|
||||
*/
|
||||
android_service_t *service;
|
||||
|
||||
/**
|
||||
* VpnService builder (accessed via JNI)
|
||||
*/
|
||||
vpnservice_builder_t *builder;
|
||||
|
||||
/**
|
||||
* CharonVpnService reference
|
||||
*/
|
||||
jobject vpn_service;
|
||||
};
|
||||
|
||||
/**
|
||||
* Single instance of charonservice_t.
|
||||
*/
|
||||
charonservice_t *charonservice;
|
||||
|
||||
/**
|
||||
* hook in library for debugging messages
|
||||
*/
|
||||
extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
|
||||
extern void (*dbg)(debug_t group, level_t level, char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Logging hook for library logs, using android specific logging
|
||||
|
@ -45,10 +95,11 @@ static void dbg_android(debug_t group, level_t level, char *fmt, ...)
|
|||
{
|
||||
va_list args;
|
||||
|
||||
if (level <= 4)
|
||||
if (level <= ANDROID_DEBUG_LEVEL)
|
||||
{
|
||||
char sgroup[16], buffer[8192];
|
||||
char *current = buffer, *next;
|
||||
|
||||
snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group);
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
|
@ -67,11 +118,277 @@ static void dbg_android(debug_t group, level_t level, char *fmt, ...)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize file logger
|
||||
*/
|
||||
static void initialize_logger(char *logfile)
|
||||
{
|
||||
file_logger_t *file_logger;
|
||||
debug_t group;
|
||||
FILE *file;
|
||||
|
||||
/* truncate an existing file */
|
||||
file = fopen(logfile, "w");
|
||||
if (!file)
|
||||
{
|
||||
DBG1(DBG_DMN, "opening file %s for logging failed: %s",
|
||||
logfile, strerror(errno));
|
||||
return;
|
||||
}
|
||||
/* flush each line */
|
||||
setlinebuf(file);
|
||||
|
||||
file_logger = file_logger_create(file, "%b %e %T", FALSE);
|
||||
for (group = 0; group < DBG_MAX; group++)
|
||||
{
|
||||
file_logger->set_level(file_logger, group, ANDROID_DEBUG_LEVEL);
|
||||
}
|
||||
charon->file_loggers->insert_last(charon->file_loggers, file_logger);
|
||||
charon->bus->add_logger(charon->bus, &file_logger->logger);
|
||||
}
|
||||
|
||||
METHOD(charonservice_t, update_status, bool,
|
||||
private_charonservice_t *this, android_vpn_state_t code)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
bool success = FALSE;
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
|
||||
"updateStatus", "(I)V");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
(*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)code);
|
||||
success = !androidjni_exception_occurred(env);
|
||||
|
||||
failed:
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return success;
|
||||
}
|
||||
|
||||
METHOD(charonservice_t, bypass_socket, bool,
|
||||
private_charonservice_t *this, int fd, int family)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
|
||||
"protect", "(I)Z");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
|
||||
{
|
||||
DBG1(DBG_CFG, "VpnService.protect() failed");
|
||||
goto failed;
|
||||
}
|
||||
androidjni_detach_thread();
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
|
||||
private_charonservice_t *this)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
jobjectArray jcerts;
|
||||
linked_list_t *list;
|
||||
jsize i;
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
method_id = (*env)->GetMethodID(env,
|
||||
android_charonvpnservice_class,
|
||||
"getTrustedCertificates", "(Ljava/lang/String;)[[B");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
jcerts = (*env)->CallObjectMethod(env, this->vpn_service, method_id, NULL);
|
||||
if (!jcerts)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
list = linked_list_create();
|
||||
for (i = 0; i < (*env)->GetArrayLength(env, jcerts); ++i)
|
||||
{
|
||||
chunk_t *ca_cert;
|
||||
jbyteArray jcert;
|
||||
|
||||
ca_cert = malloc_thing(chunk_t);
|
||||
list->insert_last(list, ca_cert);
|
||||
|
||||
jcert = (*env)->GetObjectArrayElement(env, jcerts, i);
|
||||
*ca_cert = chunk_alloc((*env)->GetArrayLength(env, jcert));
|
||||
(*env)->GetByteArrayRegion(env, jcert, 0, ca_cert->len, ca_cert->ptr);
|
||||
(*env)->DeleteLocalRef(env, jcert);
|
||||
}
|
||||
(*env)->DeleteLocalRef(env, jcerts);
|
||||
androidjni_detach_thread();
|
||||
return list;
|
||||
|
||||
failed:
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
|
||||
private_charonservice_t *this)
|
||||
{
|
||||
return this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a new connection
|
||||
*
|
||||
* @param local local ip address (gets owned)
|
||||
* @param gateway gateway address (gets owned)
|
||||
* @param username username (gets owned)
|
||||
* @param password password (gets owned)
|
||||
*/
|
||||
static void initiate(char *local, char *gateway, char *username, char *password)
|
||||
{
|
||||
private_charonservice_t *this = (private_charonservice_t*)charonservice;
|
||||
|
||||
this->creds->clear(this->creds);
|
||||
this->creds->add_username_password(this->creds, username, password);
|
||||
memwipe(password, strlen(password));
|
||||
free(password);
|
||||
|
||||
DESTROY_IF(this->service);
|
||||
this->service = android_service_create(local, gateway, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize/deinitialize Android backend
|
||||
*/
|
||||
static bool charonservice_register(void *plugin, plugin_feature_t *feature,
|
||||
bool reg, void *data)
|
||||
{
|
||||
private_charonservice_t *this = (private_charonservice_t*)charonservice;
|
||||
if (reg)
|
||||
{
|
||||
lib->credmgr->add_set(lib->credmgr, &this->creds->set);
|
||||
hydra->attributes->add_handler(hydra->attributes,
|
||||
&this->attr->handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
|
||||
hydra->attributes->remove_handler(hydra->attributes,
|
||||
&this->attr->handler);
|
||||
if (this->service)
|
||||
{
|
||||
this->service->destroy(this->service);
|
||||
this->service = NULL;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the charonservice object
|
||||
*/
|
||||
static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
|
||||
{
|
||||
private_charonservice_t *this;
|
||||
static plugin_feature_t features[] = {
|
||||
PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create),
|
||||
PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
|
||||
PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
|
||||
PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
|
||||
PLUGIN_CALLBACK((plugin_feature_callback_t)charonservice_register, NULL),
|
||||
PLUGIN_PROVIDE(CUSTOM, "Android backend"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "libcharon"),
|
||||
};
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.update_status = _update_status,
|
||||
.bypass_socket = _bypass_socket,
|
||||
.get_trusted_certificates = _get_trusted_certificates,
|
||||
.get_vpnservice_builder = _get_vpnservice_builder,
|
||||
},
|
||||
.attr = android_attr_create(),
|
||||
.creds = android_creds_create(),
|
||||
.builder = vpnservice_builder_create(builder),
|
||||
.vpn_service = (*env)->NewGlobalRef(env, service),
|
||||
);
|
||||
charonservice = &this->public;
|
||||
|
||||
lib->plugins->add_static_features(lib->plugins, "androidbridge", features,
|
||||
countof(features), TRUE);
|
||||
|
||||
lib->settings->set_int(lib->settings,
|
||||
"charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL);
|
||||
lib->settings->set_int(lib->settings,
|
||||
"charon.retransmit_tries", ANDROID_RETRASNMIT_TRIES);
|
||||
lib->settings->set_double(lib->settings,
|
||||
"charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT);
|
||||
lib->settings->set_double(lib->settings,
|
||||
"charon.retransmit_base", ANDROID_RETRANSMIT_BASE);
|
||||
lib->settings->set_bool(lib->settings,
|
||||
"charon.close_ike_on_child_failure", TRUE);
|
||||
/* setting the source address breaks the VpnService.protect() function which
|
||||
* uses SO_BINDTODEVICE internally. the addresses provided to the kernel as
|
||||
* auxiliary data have precedence over this option causing a routing loop if
|
||||
* the gateway is contained in the VPN routes. alternatively, providing an
|
||||
* explicit device (in addition or instead of the source address) in the
|
||||
* auxiliary data would also work, but we currently don't have that
|
||||
* information */
|
||||
lib->settings->set_bool(lib->settings,
|
||||
"charon.plugins.socket-default.set_source", FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize the charonservice object
|
||||
*/
|
||||
static void charonservice_deinit(JNIEnv *env)
|
||||
{
|
||||
private_charonservice_t *this = (private_charonservice_t*)charonservice;
|
||||
|
||||
this->builder->destroy(this->builder);
|
||||
this->creds->destroy(this->creds);
|
||||
this->attr->destroy(this->attr);
|
||||
(*env)->DeleteGlobalRef(env, this->vpn_service);
|
||||
free(this);
|
||||
charonservice = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle SIGSEGV/SIGILL signals raised by threads
|
||||
*/
|
||||
static void segv_handler(int signal)
|
||||
{
|
||||
dbg_android(DBG_DMN, 1, "thread %u received %d", thread_current_id(),
|
||||
signal);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize charon and the libraries via JNI
|
||||
*/
|
||||
JNI_METHOD(CharonVpnService, initializeCharon, void)
|
||||
JNI_METHOD(CharonVpnService, initializeCharon, void,
|
||||
jobject builder, jstring jlogfile)
|
||||
{
|
||||
struct sigaction action;
|
||||
struct utsname utsname;
|
||||
char *logfile;
|
||||
|
||||
/* logging for library during initialization, as we have no bus yet */
|
||||
dbg = dbg_android;
|
||||
|
||||
|
@ -97,8 +414,7 @@ JNI_METHOD(CharonVpnService, initializeCharon, void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!libcharon_init("charon") ||
|
||||
!charon->initialize(charon, PLUGINS))
|
||||
if (!libcharon_init("charon"))
|
||||
{
|
||||
libcharon_deinit();
|
||||
libipsec_deinit();
|
||||
|
@ -107,18 +423,69 @@ JNI_METHOD(CharonVpnService, initializeCharon, void)
|
|||
return;
|
||||
}
|
||||
|
||||
logfile = androidjni_convert_jstring(env, jlogfile);
|
||||
initialize_logger(logfile);
|
||||
free(logfile);
|
||||
|
||||
charonservice_init(env, this, builder);
|
||||
|
||||
if (uname(&utsname) != 0)
|
||||
{
|
||||
memset(&utsname, 0, sizeof(utsname));
|
||||
}
|
||||
DBG1(DBG_DMN, "Starting IKE charon daemon (strongSwan "VERSION", %s %s, %s)",
|
||||
utsname.sysname, utsname.release, utsname.machine);
|
||||
|
||||
if (!charon->initialize(charon, PLUGINS))
|
||||
{
|
||||
libcharon_deinit();
|
||||
charonservice_deinit(env);
|
||||
libipsec_deinit();
|
||||
libhydra_deinit();
|
||||
library_deinit();
|
||||
return;
|
||||
}
|
||||
|
||||
/* add handler for SEGV and ILL etc. */
|
||||
action.sa_handler = segv_handler;
|
||||
action.sa_flags = 0;
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaction(SIGSEGV, &action, NULL);
|
||||
sigaction(SIGILL, &action, NULL);
|
||||
sigaction(SIGBUS, &action, NULL);
|
||||
action.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &action, NULL);
|
||||
|
||||
/* start daemon (i.e. the threads in the thread-pool) */
|
||||
charon->start(charon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize charon and the libraries via JNI
|
||||
* Deinitialize charon and all libraries
|
||||
*/
|
||||
JNI_METHOD(CharonVpnService, deinitializeCharon, void)
|
||||
{
|
||||
/* deinitialize charon before we destroy our own objects */
|
||||
libcharon_deinit();
|
||||
charonservice_deinit(env);
|
||||
libipsec_deinit();
|
||||
libhydra_deinit();
|
||||
library_deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate SA
|
||||
*/
|
||||
JNI_METHOD(CharonVpnService, initiate, void,
|
||||
jstring jlocal_address, jstring jgateway, jstring jusername,
|
||||
jstring jpassword)
|
||||
{
|
||||
char *local_address, *gateway, *username, *password;
|
||||
|
||||
local_address = androidjni_convert_jstring(env, jlocal_address);
|
||||
gateway = androidjni_convert_jstring(env, jgateway);
|
||||
username = androidjni_convert_jstring(env, jusername);
|
||||
password = androidjni_convert_jstring(env, jpassword);
|
||||
|
||||
initiate(local_address, gateway, username, password);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 <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 libandroidbridge libandroidbridge
|
||||
*
|
||||
* @defgroup android_backend backend
|
||||
* @ingroup libandroidbridge
|
||||
*
|
||||
* @defgroup android_kernel kernel
|
||||
* @ingroup libandroidbridge
|
||||
*
|
||||
* @defgroup charonservice charonservice
|
||||
* @{ @ingroup libandroidbridge
|
||||
*/
|
||||
|
||||
#ifndef CHARONSERVICE_H_
|
||||
#define CHARONSERVICE_H_
|
||||
|
||||
#include "vpnservice_builder.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
typedef enum android_vpn_state_t android_vpn_state_t;
|
||||
typedef struct charonservice_t charonservice_t;
|
||||
|
||||
/**
|
||||
* VPN status codes. As defined in CharonVpnService.java
|
||||
*/
|
||||
enum android_vpn_state_t {
|
||||
CHARONSERVICE_CHILD_STATE_UP = 1,
|
||||
CHARONSERVICE_CHILD_STATE_DOWN,
|
||||
CHARONSERVICE_AUTH_ERROR,
|
||||
CHARONSERVICE_PEER_AUTH_ERROR,
|
||||
CHARONSERVICE_LOOKUP_ERROR,
|
||||
CHARONSERVICE_UNREACHABLE_ERROR,
|
||||
CHARONSERVICE_GENERIC_ERROR,
|
||||
};
|
||||
|
||||
/**
|
||||
* Public interface of charonservice.
|
||||
*
|
||||
* Used to communicate with CharonVpnService via JNI
|
||||
*/
|
||||
struct charonservice_t {
|
||||
|
||||
/**
|
||||
* Update the status in the Java domain (UI)
|
||||
*
|
||||
* @param code status code
|
||||
* @return TRUE on success
|
||||
*/
|
||||
bool (*update_status)(charonservice_t *this, android_vpn_state_t code);
|
||||
|
||||
/**
|
||||
* Install a bypass policy for the given socket using the protect() Method
|
||||
* of the Android VpnService interface
|
||||
*
|
||||
* @param fd socket file descriptor
|
||||
* @param family socket protocol family
|
||||
* @return TRUE if operation successful
|
||||
*/
|
||||
bool (*bypass_socket)(charonservice_t *this, int fd, int family);
|
||||
|
||||
/**
|
||||
* Get a list of trusted certificates via JNI
|
||||
*
|
||||
* @return list of DER encoded certificates (as chunk_t*),
|
||||
* NULL on failure
|
||||
*/
|
||||
linked_list_t *(*get_trusted_certificates)(charonservice_t *this);
|
||||
|
||||
/**
|
||||
* Get the current vpnservice_builder_t object
|
||||
*
|
||||
* @return VpnService.Builder instance
|
||||
*/
|
||||
vpnservice_builder_t *(*get_vpnservice_builder)(charonservice_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The single instance of charonservice_t.
|
||||
*
|
||||
* Set between JNI calls to initializeCharon() and deinitializeCharon().
|
||||
*/
|
||||
extern charonservice_t *charonservice;
|
||||
|
||||
#endif /** CHARONSERVICE_H_ @}*/
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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 <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 "android_ipsec.h"
|
||||
#include "../charonservice.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <library.h>
|
||||
#include <hydra.h>
|
||||
#include <ipsec.h>
|
||||
|
||||
typedef struct private_kernel_android_ipsec_t private_kernel_android_ipsec_t;
|
||||
|
||||
struct private_kernel_android_ipsec_t {
|
||||
|
||||
/**
|
||||
* Public kernel interface
|
||||
*/
|
||||
kernel_android_ipsec_t public;
|
||||
|
||||
/**
|
||||
* Listener for lifetime expire events
|
||||
*/
|
||||
ipsec_event_listener_t ipsec_listener;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback registrered with libipsec.
|
||||
*/
|
||||
void expire(u_int32_t reqid, u_int8_t protocol, u_int32_t spi, bool hard)
|
||||
{
|
||||
hydra->kernel_interface->expire(hydra->kernel_interface, reqid, protocol,
|
||||
spi, hard);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, get_spi, status_t,
|
||||
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
|
||||
u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
|
||||
{
|
||||
return ipsec->sas->get_spi(ipsec->sas, src, dst, protocol, reqid, spi);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, get_cpi, status_t,
|
||||
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
|
||||
u_int32_t reqid, u_int16_t *cpi)
|
||||
{
|
||||
return NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, add_sa, status_t,
|
||||
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
|
||||
u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
|
||||
u_int32_t tfc, lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
|
||||
u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
|
||||
u_int16_t cpi, bool encap, bool esn, bool inbound,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
|
||||
{
|
||||
return ipsec->sas->add_sa(ipsec->sas, src, dst, spi, protocol, reqid, mark,
|
||||
tfc, lifetime, enc_alg, enc_key, int_alg, int_key,
|
||||
mode, ipcomp, cpi, encap, esn, inbound, src_ts,
|
||||
dst_ts);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, update_sa, status_t,
|
||||
private_kernel_android_ipsec_t *this, u_int32_t spi, u_int8_t protocol,
|
||||
u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
|
||||
bool encap, bool new_encap, mark_t mark)
|
||||
{
|
||||
return NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, query_sa, status_t,
|
||||
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
|
||||
u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes)
|
||||
{
|
||||
return NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, del_sa, status_t,
|
||||
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
|
||||
u_int32_t spi, u_int8_t protocol, u_int16_t cpi, mark_t mark)
|
||||
{
|
||||
return ipsec->sas->del_sa(ipsec->sas, src, dst, spi, protocol, cpi, mark);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, flush_sas, status_t,
|
||||
private_kernel_android_ipsec_t *this)
|
||||
{
|
||||
return ipsec->sas->flush_sas(ipsec->sas);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, add_policy, status_t,
|
||||
private_kernel_android_ipsec_t *this, host_t *src, host_t *dst,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
|
||||
policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
|
||||
policy_priority_t priority)
|
||||
{
|
||||
return ipsec->policies->add_policy(ipsec->policies, src, dst, src_ts,
|
||||
dst_ts, direction, type, sa, mark,
|
||||
priority);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, query_policy, status_t,
|
||||
private_kernel_android_ipsec_t *this, traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark,
|
||||
u_int32_t *use_time)
|
||||
{
|
||||
return NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, del_policy, status_t,
|
||||
private_kernel_android_ipsec_t *this, traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
|
||||
mark_t mark, policy_priority_t priority)
|
||||
{
|
||||
return ipsec->policies->del_policy(ipsec->policies, src_ts, dst_ts,
|
||||
direction, reqid, mark, priority);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, flush_policies, status_t,
|
||||
private_kernel_android_ipsec_t *this)
|
||||
{
|
||||
ipsec->policies->flush_policies(ipsec->policies);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, bypass_socket, bool,
|
||||
private_kernel_android_ipsec_t *this, int fd, int family)
|
||||
{
|
||||
return charonservice->bypass_socket(charonservice, fd, family);
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, enable_udp_decap, bool,
|
||||
private_kernel_android_ipsec_t *this, int fd, int family, u_int16_t port)
|
||||
{
|
||||
return NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
METHOD(kernel_ipsec_t, destroy, void,
|
||||
private_kernel_android_ipsec_t *this)
|
||||
{
|
||||
ipsec->events->unregister_listener(ipsec->events, &this->ipsec_listener);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header.
|
||||
*/
|
||||
kernel_android_ipsec_t *kernel_android_ipsec_create()
|
||||
{
|
||||
private_kernel_android_ipsec_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.interface = {
|
||||
.get_spi = _get_spi,
|
||||
.get_cpi = _get_cpi,
|
||||
.add_sa = _add_sa,
|
||||
.update_sa = _update_sa,
|
||||
.query_sa = _query_sa,
|
||||
.del_sa = _del_sa,
|
||||
.flush_sas = _flush_sas,
|
||||
.add_policy = _add_policy,
|
||||
.query_policy = _query_policy,
|
||||
.del_policy = _del_policy,
|
||||
.flush_policies = _flush_policies,
|
||||
.bypass_socket = _bypass_socket,
|
||||
.enable_udp_decap = _enable_udp_decap,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
.ipsec_listener = {
|
||||
.expire = expire,
|
||||
},
|
||||
);
|
||||
|
||||
ipsec->events->register_listener(ipsec->events, &this->ipsec_listener);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 <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 kernel_android_ipsec kernel_android_ipsec
|
||||
* @{ @ingroup kernel_android
|
||||
*/
|
||||
|
||||
#ifndef KERNEL_ANDROID_IPSEC_H_
|
||||
#define KERNEL_ANDROID_IPSEC_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <kernel/kernel_ipsec.h>
|
||||
|
||||
typedef struct kernel_android_ipsec_t kernel_android_ipsec_t;
|
||||
|
||||
/**
|
||||
* Implementation of the ipsec interface using libipsec on Android
|
||||
*/
|
||||
struct kernel_android_ipsec_t {
|
||||
|
||||
/**
|
||||
* Implements kernel_ipsec_t interface
|
||||
*/
|
||||
kernel_ipsec_t interface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a android ipsec interface instance.
|
||||
*
|
||||
* @return kernel_android_ipsec_t instance
|
||||
*/
|
||||
kernel_android_ipsec_t *kernel_android_ipsec_create();
|
||||
|
||||
#endif /** KERNEL_ANDROID_IPSEC_H_ @}*/
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 <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 "android_net.h"
|
||||
|
||||
typedef struct private_kernel_android_net_t private_kernel_android_net_t;
|
||||
|
||||
struct private_kernel_android_net_t {
|
||||
|
||||
/**
|
||||
* Public kernel interface
|
||||
*/
|
||||
kernel_android_net_t public;
|
||||
};
|
||||
|
||||
METHOD(kernel_net_t, add_ip, status_t,
|
||||
private_kernel_android_net_t *this, host_t *virtual_ip, host_t *iface_ip)
|
||||
{
|
||||
/* we get the IP from the IKE_SA once the CHILD_SA is established */
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(kernel_net_t, destroy, void,
|
||||
private_kernel_android_net_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header.
|
||||
*/
|
||||
kernel_android_net_t *kernel_android_net_create()
|
||||
{
|
||||
private_kernel_android_net_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.interface = {
|
||||
.get_source_addr = (void*)return_null,
|
||||
.get_nexthop = (void*)return_null,
|
||||
.get_interface = (void*)return_null,
|
||||
.create_address_enumerator = (void*)enumerator_create_empty,
|
||||
.add_ip = _add_ip,
|
||||
.del_ip = (void*)return_failed,
|
||||
.add_route = (void*)return_failed,
|
||||
.del_route = (void*)return_failed,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 <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 kernel_android_net kernel_android_net
|
||||
* @{ @ingroup kernel_android
|
||||
*/
|
||||
|
||||
#ifndef KERNEL_ANDROID_NET_H_
|
||||
#define KERNEL_ANDROID_NET_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <kernel/kernel_net.h>
|
||||
|
||||
typedef struct kernel_android_net_t kernel_android_net_t;
|
||||
|
||||
/**
|
||||
* Implementation of the kernel-net interface. This currently consists of only
|
||||
* noops because a kernel_net_t implementation is required and we can't use
|
||||
* kernel_netlink_net_t at the moment.
|
||||
*/
|
||||
struct kernel_android_net_t {
|
||||
|
||||
/**
|
||||
* Implements kernel_net_t interface
|
||||
*/
|
||||
kernel_net_t interface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a android net interface instance.
|
||||
*
|
||||
* @return kernel_android_net_t instance
|
||||
*/
|
||||
kernel_android_net_t *kernel_android_net_create();
|
||||
|
||||
#endif /** KERNEL_ANDROID_NET_H_ @}*/
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* 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 <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 "vpnservice_builder.h"
|
||||
#include "android_jni.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <library.h>
|
||||
|
||||
typedef struct private_vpnservice_builder_t private_vpnservice_builder_t;
|
||||
|
||||
/**
|
||||
* private data of vpnservice_builder
|
||||
*/
|
||||
struct private_vpnservice_builder_t {
|
||||
|
||||
/**
|
||||
* public interface
|
||||
*/
|
||||
vpnservice_builder_t public;
|
||||
|
||||
/**
|
||||
* Java object
|
||||
*/
|
||||
jobject builder;
|
||||
};
|
||||
|
||||
METHOD(vpnservice_builder_t, add_address, bool,
|
||||
private_vpnservice_builder_t *this, host_t *addr)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
jstring str;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
DBG2(DBG_LIB, "builder: adding interface address %H", addr);
|
||||
|
||||
if (addr->get_family(addr) != AF_INET)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (snprintf(buf, sizeof(buf), "%H", addr) >= sizeof(buf))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_builder_class,
|
||||
"addAddress", "(Ljava/lang/String;I)Z");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
str = (*env)->NewStringUTF(env, buf);
|
||||
if (!str)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (!(*env)->CallBooleanMethod(env, this->builder, method_id, str, 32))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
androidjni_detach_thread();
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
DBG1(DBG_LIB, "builder: failed to add address");
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(vpnservice_builder_t, set_mtu, bool,
|
||||
private_vpnservice_builder_t *this, int mtu)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
DBG2(DBG_LIB, "builder: setting MTU to %d", mtu);
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_builder_class,
|
||||
"setMtu", "(I)Z");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (!(*env)->CallBooleanMethod(env, this->builder, method_id, mtu))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
androidjni_detach_thread();
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
DBG1(DBG_LIB, "builder: failed to set MTU");
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(vpnservice_builder_t, add_route, bool,
|
||||
private_vpnservice_builder_t *this, host_t *net, int prefix)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
jstring str;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
DBG2(DBG_LIB, "builder: adding route %+H/%d", net, prefix);
|
||||
|
||||
if (net->get_family(net) != AF_INET)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (snprintf(buf, sizeof(buf), "%+H", net) >= sizeof(buf))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_builder_class,
|
||||
"addRoute", "(Ljava/lang/String;I)Z");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
str = (*env)->NewStringUTF(env, buf);
|
||||
if (!str)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (!(*env)->CallBooleanMethod(env, this->builder, method_id, str, prefix))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
androidjni_detach_thread();
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
DBG1(DBG_LIB, "builder: failed to add route");
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(vpnservice_builder_t, add_dns, bool,
|
||||
private_vpnservice_builder_t *this, host_t *dns)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
jstring str;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
DBG2(DBG_LIB, "builder: adding DNS server %H", dns);
|
||||
|
||||
if (dns->get_family(dns) != AF_INET)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (snprintf(buf, sizeof(buf), "%H", dns) >= sizeof(buf))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_builder_class,
|
||||
"addDnsServer", "(Ljava/lang/String;)Z");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
str = (*env)->NewStringUTF(env, buf);
|
||||
if (!str)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
if (!(*env)->CallBooleanMethod(env, this->builder, method_id, str))
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
androidjni_detach_thread();
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
DBG1(DBG_LIB, "builder: failed to add DNS server");
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(vpnservice_builder_t, establish, int,
|
||||
private_vpnservice_builder_t *this)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jmethodID method_id;
|
||||
int fd;
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
|
||||
DBG2(DBG_LIB, "builder: building TUN device");
|
||||
|
||||
method_id = (*env)->GetMethodID(env, android_charonvpnservice_builder_class,
|
||||
"establish", "()I");
|
||||
if (!method_id)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
fd = (*env)->CallIntMethod(env, this->builder, method_id);
|
||||
if (fd == -1)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
androidjni_detach_thread();
|
||||
return fd;
|
||||
|
||||
failed:
|
||||
DBG1(DBG_LIB, "builder: failed to build TUN device");
|
||||
androidjni_exception_occurred(env);
|
||||
androidjni_detach_thread();
|
||||
return -1;
|
||||
}
|
||||
|
||||
METHOD(vpnservice_builder_t, destroy, void,
|
||||
private_vpnservice_builder_t *this)
|
||||
{
|
||||
JNIEnv *env;
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
(*env)->DeleteGlobalRef(env, this->builder);
|
||||
androidjni_detach_thread();
|
||||
free(this);
|
||||
}
|
||||
|
||||
vpnservice_builder_t *vpnservice_builder_create(jobject builder)
|
||||
{
|
||||
JNIEnv *env;
|
||||
private_vpnservice_builder_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.add_address = _add_address,
|
||||
.add_route = _add_route,
|
||||
.add_dns = _add_dns,
|
||||
.set_mtu = _set_mtu,
|
||||
.establish = _establish,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
);
|
||||
|
||||
androidjni_attach_thread(&env);
|
||||
this->builder = (*env)->NewGlobalRef(env, builder);
|
||||
androidjni_detach_thread();
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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 <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 vpnservice_builder vpnservice_builder
|
||||
* @{ @ingroup libandroidbridge
|
||||
*/
|
||||
|
||||
#ifndef VPNSERVICE_BUILDER_H_
|
||||
#define VPNSERVICE_BUILDER_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/host.h>
|
||||
|
||||
typedef struct vpnservice_builder_t vpnservice_builder_t;
|
||||
|
||||
/**
|
||||
* VpnService.Builder, used to build a TUN device.
|
||||
*
|
||||
* Communicates with CharonVpnService.BuilderAdapter via JNI
|
||||
*/
|
||||
struct vpnservice_builder_t {
|
||||
|
||||
/**
|
||||
* Add an interface address
|
||||
*
|
||||
* @param addr the desired interface address
|
||||
* @return TRUE on success
|
||||
*/
|
||||
bool (*add_address)(vpnservice_builder_t *this, host_t *addr);
|
||||
|
||||
/**
|
||||
* Add a route
|
||||
*
|
||||
* @param net the network address
|
||||
* @param prefix_length the prefix length
|
||||
* @return TRUE on success
|
||||
*/
|
||||
bool (*add_route)(vpnservice_builder_t *this, host_t *net, int prefix);
|
||||
|
||||
/**
|
||||
* Add a DNS server
|
||||
*
|
||||
* @param dns the address of the DNS server
|
||||
* @return TRUE on success
|
||||
*/
|
||||
bool (*add_dns)(vpnservice_builder_t *this, host_t *dns);
|
||||
|
||||
/**
|
||||
* Set the MTU for the TUN device
|
||||
*
|
||||
* @param mtu the MTU to set
|
||||
* @return TRUE on success
|
||||
*/
|
||||
bool (*set_mtu)(vpnservice_builder_t *this, int mtu);
|
||||
|
||||
/**
|
||||
* Build the TUN device
|
||||
*
|
||||
* @return the TUN file descriptor, -1 if failed
|
||||
*/
|
||||
int (*establish)(vpnservice_builder_t *this);
|
||||
|
||||
/**
|
||||
* Destroy a vpnservice_builder
|
||||
*/
|
||||
void (*destroy)(vpnservice_builder_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a vpnservice_builder instance
|
||||
*
|
||||
* @param builder CharonVpnService.BuilderAdapter object
|
||||
* @return vpnservice_builder_t instance
|
||||
*/
|
||||
vpnservice_builder_t *vpnservice_builder_create(jobject builder);
|
||||
|
||||
#endif /** VPNSERVICE_BUILDER_H_ @}*/
|
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<solid
|
||||
android:color="#333" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<fragment
|
||||
class="org.strongswan.android.ui.LogFragment"
|
||||
android:id="@+id/log_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<org.strongswan.android.ui.LogScrollView
|
||||
android:id="@+id/scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="10dp"
|
||||
android:scrollbarFadeDuration="0"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="true" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/log_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="9sp"
|
||||
android:typeface="monospace" >
|
||||
</TextView>
|
||||
|
||||
</org.strongswan.android.ui.LogScrollView>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<LinearLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="5dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<TextView
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/profile_username_label"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/username"
|
||||
android:enabled="false"
|
||||
android:inputType="none" />
|
||||
|
||||
<TextView
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/profile_password_label"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/password"
|
||||
android:inputType="textPassword|textNoSuggestions"
|
||||
android:singleLine="true" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,12 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hello" />
|
||||
<fragment
|
||||
class="org.strongswan.android.ui.VpnStateFragment"
|
||||
android:id="@+id/vpn_state_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
<fragment
|
||||
class="org.strongswan.android.ui.VpnProfileListFragment"
|
||||
android:id="@+id/profile_list_frag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/profile_name_label" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:hint="@string/profile_name_hint" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/profile_gateway_label" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/gateway"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:inputType="textNoSuggestions" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/profile_username_label" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:inputType="textNoSuggestions" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/profile_password_label" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:inputType="textPassword|textNoSuggestions"
|
||||
android:hint="@string/profile_password_hint" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/profile_ca_label" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/ca_auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/profile_ca_auto_label" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/ca_show_all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="@string/profile_ca_show_all" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/ca_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp" >
|
||||
|
||||
<ListView
|
||||
android:id="@+id/profile_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:dividerHeight="1dp"
|
||||
android:divider="?android:attr/listDivider"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="true" />
|
||||
|
||||
<TextView android:id="@+id/profile_list_empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:text="@string/no_profiles"/>
|
||||
|
||||
</FrameLayout>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="6dip"
|
||||
android:paddingTop="4dip"
|
||||
android:background="?android:attr/activatedBackgroundIndicator" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_gateway"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:layout_marginLeft="15dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/profile_item_username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:layout_marginLeft="15dp" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/certificate_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textSize="18sp" />
|
||||
|
||||
</TableLayout>
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/vpn_state_background"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:columnCount="2"
|
||||
android:rowCount="2" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="5dp"
|
||||
android:gravity="top"
|
||||
android:text="@string/state_label"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/vpn_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:text="@string/state_disabled"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/vpn_profile_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="5dp"
|
||||
android:gravity="top"
|
||||
android:text="@string/profile_label"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/vpn_profile_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone" >
|
||||
</TextView>
|
||||
</GridLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/action"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:text="@string/disconnect"
|
||||
style="?android:attr/borderlessButtonStyle" >
|
||||
</Button>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_send_log"
|
||||
android:title="@string/send_log"
|
||||
android:showAsAction="ifRoom|withText" />
|
||||
|
||||
</menu>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_reload_certs"
|
||||
android:title="@string/reload_trusted_certs"
|
||||
android:showAsAction="withText" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_show_log"
|
||||
android:title="@string/show_log"
|
||||
android:showAsAction="withText" />
|
||||
|
||||
</menu>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_accept"
|
||||
android:title="@string/profile_edit_save"
|
||||
android:showAsAction="always|withText" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_cancel"
|
||||
android:title="@string/profile_edit_cancel"
|
||||
android:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item android:id="@+id/add_profile"
|
||||
android:title="@string/add_profile"
|
||||
android:showAsAction="always|withText" />
|
||||
|
||||
</menu>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item android:id="@+id/edit_profile"
|
||||
android:title="@string/edit_profile" ></item>
|
||||
|
||||
<item android:id="@+id/delete_profile"
|
||||
android:title="@string/delete_profile" ></item>
|
||||
|
||||
</menu>
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<!-- Application -->
|
||||
<string name="app_name">strongSwan VPN Client</string>
|
||||
<string name="main_activity_name">strongSwan</string>
|
||||
<string name="reload_trusted_certs">CA-Zertifikate neu laden</string>
|
||||
<string name="show_log">Log anzeigen</string>
|
||||
|
||||
<!-- Log view -->
|
||||
<string name="log_title">Log</string>
|
||||
<string name="send_log">Logdatei senden</string>
|
||||
<string name="empty_log">Logdatei ist leer</string>
|
||||
<string name="log_mail_subject">strongSwan %1$s Logdatei</string>
|
||||
|
||||
<!-- VPN profile list -->
|
||||
<string name="no_profiles">Keine VPN Profile vorhanden.</string>
|
||||
<string name="add_profile">Profil hinzufügen</string>
|
||||
<string name="edit_profile">Bearbeiten</string>
|
||||
<string name="delete_profile">Löschen</string>
|
||||
<string name="select_profiles">Profile auswählen</string>
|
||||
<string name="profiles_deleted">Ausgewählte Profile gelöscht</string>
|
||||
<string name="no_profile_selected">Kein Profil ausgewählt</string>
|
||||
<string name="one_profile_selected">Ein Profil ausgewählt</string>
|
||||
<string name="x_profiles_selected">%1$d Profile ausgewählt</string>
|
||||
|
||||
<!-- VPN profile details -->
|
||||
<string name="profile_edit_save">Speichern</string>
|
||||
<string name="profile_edit_cancel">Abbrechen</string>
|
||||
<string name="profile_name_label">Profilname:</string>
|
||||
<string name="profile_name_hint">(Gateway-Adresse verwenden)</string>
|
||||
<string name="profile_gateway_label">Gateway:</string>
|
||||
<string name="profile_username_label">Benutzername:</string>
|
||||
<string name="profile_password_label">Passwort:</string>
|
||||
<string name="profile_password_hint">(anfordern wenn benötigt)</string>
|
||||
<string name="profile_ca_label">CA-Zertifikat:</string>
|
||||
<string name="profile_ca_auto_label">Automatisch wählen</string>
|
||||
<string name="profile_ca_show_all">Alle Zertifikate anzeigen</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">Bitte geben Sie hier die Gateway-Adresse ein</string>
|
||||
<string name="alert_text_no_input_username">Bitte geben Sie hier Ihren Benutzernamen ein</string>
|
||||
<string name="alert_text_nocertfound_title">Kein CA-Zertifikat ausgewählt</string>
|
||||
<string name="alert_text_nocertfound">Bitte wählen Sie eines aus oder aktivieren Sie <i>Automatisch wählen</i></string>
|
||||
|
||||
<!-- VPN state fragment -->
|
||||
<string name="state_label">Status:</string>
|
||||
<string name="profile_label">Profil:</string>
|
||||
<string name="disconnect">Trennen</string>
|
||||
<string name="state_connecting">Verbinden…</string>
|
||||
<string name="state_connected">Verbunden</string>
|
||||
<string name="state_disconnecting">Trennen…</string>
|
||||
<string name="state_disabled">Kein aktives Profil</string>
|
||||
<string name="state_error">Fehler</string>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<string name="login_title">Passwort eingeben um zu verbinden</string>
|
||||
<string name="login_confirm">Verbinden</string>
|
||||
<string name="error_introduction">Fehler beim Aufsetzen des VPN:</string>
|
||||
<string name="error_lookup_failed">Gateway-Adresse konnte nicht aufgelöst werden.</string>
|
||||
<string name="error_unreachable">Gateway ist nicht erreichbar.</string>
|
||||
<string name="error_peer_auth_failed">Authentifizierung des Gateway ist fehlgeschlagen.</string>
|
||||
<string name="error_auth_failed">Benutzerauthentifizierung ist fehlgeschlagen.</string>
|
||||
<string name="error_generic">Unbekannter Fehler während des Verbindens.</string>
|
||||
<string name="connecting_title">Verbinden: %1$s</string>
|
||||
<string name="connecting_message">Verbinde mit \""%1$s\".</string>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<color
|
||||
name="error_text">#D9192C</color>
|
||||
|
||||
<color
|
||||
name="success_text">#99CC00</color>
|
||||
|
||||
</resources>
|
|
@ -1,7 +1,83 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<string name="hello">Hello World, strongSwanActivity!</string>
|
||||
<string name="app_name">strongSwan</string>
|
||||
<!-- Application -->
|
||||
<string name="app_name">strongSwan VPN Client</string>
|
||||
<string name="main_activity_name">strongSwan</string>
|
||||
<string name="reload_trusted_certs">Reload CA certificates</string>
|
||||
<string name="show_log">View log</string>
|
||||
|
||||
</resources>
|
||||
<!-- Log view -->
|
||||
<string name="log_title">Log</string>
|
||||
<string name="send_log">Send log file</string>
|
||||
<string name="empty_log">Log file is empty</string>
|
||||
<string name="log_mail_subject">strongSwan %1$s Log File</string>
|
||||
|
||||
<!-- VPN profile list -->
|
||||
<string name="no_profiles">No VPN profiles.</string>
|
||||
<string name="add_profile">Add VPN profile</string>
|
||||
<string name="edit_profile">Edit</string>
|
||||
<string name="delete_profile">Delete</string>
|
||||
<string name="select_profiles">Select profiles</string>
|
||||
<string name="profiles_deleted">Selected profiles deleted</string>
|
||||
<string name="no_profile_selected">No profile selected</string>
|
||||
<string name="one_profile_selected">One profile selected</string>
|
||||
<string name="x_profiles_selected">%1$d profiles selected"</string>
|
||||
|
||||
<!-- VPN profile details -->
|
||||
<string name="profile_edit_save">Save</string>
|
||||
<string name="profile_edit_cancel">Cancel</string>
|
||||
<string name="profile_name_label">Profile Name:</string>
|
||||
<string name="profile_name_hint">(use gateway address)</string>
|
||||
<string name="profile_gateway_label">Gateway:</string>
|
||||
<string name="profile_username_label">Username:</string>
|
||||
<string name="profile_password_label">Password:</string>
|
||||
<string name="profile_password_hint">(prompt when needed)</string>
|
||||
<string name="profile_ca_label">CA certificate:</string>
|
||||
<string name="profile_ca_auto_label">Select automatically</string>
|
||||
<string name="profile_ca_show_all">Show all certificates</string>
|
||||
<!-- Warnings/Notifications in the details view -->
|
||||
<string name="alert_text_no_input_gateway">Please enter the gateway address here</string>
|
||||
<string name="alert_text_no_input_username">Please enter your username here</string>
|
||||
<string name="alert_text_nocertfound_title">No CA certificate selected</string>
|
||||
<string name="alert_text_nocertfound">Please select one or activate <i>Select automatically</i></string>
|
||||
|
||||
<!-- VPN state fragment -->
|
||||
<string name="state_label">Status:</string>
|
||||
<string name="profile_label">Profile:</string>
|
||||
<string name="disconnect">Disconnect</string>
|
||||
<string name="state_connecting">Connecting…</string>
|
||||
<string name="state_connected">Connected</string>
|
||||
<string name="state_disconnecting">Disconnecting…</string>
|
||||
<string name="state_disabled">No active VPN</string>
|
||||
<string name="state_error">Error</string>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<string name="login_title">Enter password to connect</string>
|
||||
<string name="login_confirm">Connect</string>
|
||||
<string name="error_introduction">Failed to establish VPN:</string>
|
||||
<string name="error_lookup_failed">Gateway address lookup failed.</string>
|
||||
<string name="error_unreachable">Gateway is unreachable.</string>
|
||||
<string name="error_peer_auth_failed">Verifying gateway authentication failed.</string>
|
||||
<string name="error_auth_failed">User authentication failed.</string>
|
||||
<string name="error_generic">Unspecified failure while connecting.</string>
|
||||
<string name="connecting_title">Connecting: %1$s</string>
|
||||
<string name="connecting_message">Establishing VPN with \""%1$s\".</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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 <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.
|
||||
-->
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="ApplicationTheme" parent="@android:style/Theme.Holo">
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -1,57 +0,0 @@
|
|||
package org.strongswan.android;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.VpnService;
|
||||
|
||||
public class CharonVpnService extends VpnService
|
||||
{
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId)
|
||||
{
|
||||
// called whenever the service is started with startService
|
||||
// create our own thread because we are running in the calling processes
|
||||
// main thread
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
// onCreate is only called once
|
||||
initializeCharon();
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
// called once the service is to be destroyed
|
||||
deinitializeCharon();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of charon, provided by libandroidbridge.so
|
||||
*/
|
||||
public native void initializeCharon();
|
||||
|
||||
/**
|
||||
* Deinitialize charon, provided by libandroidbridge.so
|
||||
*/
|
||||
public native void deinitializeCharon();
|
||||
|
||||
/*
|
||||
* The libraries are extracted to /data/data/org.strongswan.android/...
|
||||
* during installation.
|
||||
*/
|
||||
static
|
||||
{
|
||||
System.loadLibrary("crypto");
|
||||
System.loadLibrary("strongswan");
|
||||
System.loadLibrary("hydra");
|
||||
System.loadLibrary("charon");
|
||||
System.loadLibrary("ipsec");
|
||||
System.loadLibrary("androidbridge");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.strongswan.android.logic.CharonVpnService;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.OpenableColumns;
|
||||
|
||||
public class LogContentProvider extends ContentProvider
|
||||
{
|
||||
private static final String AUTHORITY = "org.strongswan.android.content.log";
|
||||
/* an Uri is valid for 30 minutes */
|
||||
private static final long URI_VALIDITY = 30 * 60 * 1000;
|
||||
private static ConcurrentHashMap<Uri, Long> mUris = new ConcurrentHashMap<Uri, Long>();
|
||||
private File mLogFile;
|
||||
|
||||
public LogContentProvider()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate()
|
||||
{
|
||||
mLogFile = new File(getContext().getFilesDir(), CharonVpnService.LOG_FILE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The log file can only be accessed by Uris created with this method
|
||||
* @return null if failed to create the Uri
|
||||
*/
|
||||
public static Uri createContentUri()
|
||||
{
|
||||
SecureRandom random;
|
||||
try
|
||||
{
|
||||
random = SecureRandom.getInstance("SHA1PRNG");
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Uri uri = Uri.parse("content://" + AUTHORITY + "/" + random.nextLong());
|
||||
mUris.put(uri, SystemClock.uptimeMillis());
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri)
|
||||
{
|
||||
/* MIME type for our log file */
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder)
|
||||
{
|
||||
/* this is called by apps to find out the name and size of the file.
|
||||
* since we only provide a single file this is simple to implement */
|
||||
if (projection == null || projection.length < 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Long timestamp = mUris.get(uri);
|
||||
if (timestamp == null)
|
||||
{ /* don't check the validity as this information is not really private */
|
||||
return null;
|
||||
}
|
||||
MatrixCursor cursor = new MatrixCursor(projection, 1);
|
||||
if (OpenableColumns.DISPLAY_NAME.equals(cursor.getColumnName(0)))
|
||||
{
|
||||
cursor.newRow().add(CharonVpnService.LOG_FILE);
|
||||
}
|
||||
else if (OpenableColumns.SIZE.equals(cursor.getColumnName(0)))
|
||||
{
|
||||
cursor.newRow().add(mLogFile.length());
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
|
||||
{
|
||||
Long timestamp = mUris.get(uri);
|
||||
if (timestamp != null)
|
||||
{
|
||||
long elapsed = SystemClock.uptimeMillis() - timestamp;
|
||||
if (elapsed > 0 && elapsed < URI_VALIDITY)
|
||||
{ /* we fail if clock wrapped, should happen rarely though */
|
||||
return ParcelFileDescriptor.open(mLogFile, ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
mUris.remove(uri);
|
||||
}
|
||||
return super.openFile(uri, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values)
|
||||
{
|
||||
/* not supported */
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs)
|
||||
{
|
||||
/* not supported */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs)
|
||||
{
|
||||
/* not supported */
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
public class VpnProfile implements Cloneable
|
||||
{
|
||||
private String mName, mGateway, mUsername, mPassword, mCertificate;
|
||||
private long mId = -1;
|
||||
|
||||
public long getId()
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setId(long id)
|
||||
{
|
||||
this.mId = id;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
this.mName = name;
|
||||
}
|
||||
|
||||
public String getGateway()
|
||||
{
|
||||
return mGateway;
|
||||
}
|
||||
|
||||
public void setGateway(String gateway)
|
||||
{
|
||||
this.mGateway = gateway;
|
||||
}
|
||||
|
||||
public String getUsername()
|
||||
{
|
||||
return mUsername;
|
||||
}
|
||||
|
||||
public void setUsername(String username)
|
||||
{
|
||||
this.mUsername = username;
|
||||
}
|
||||
|
||||
public String getPassword()
|
||||
{
|
||||
return mPassword;
|
||||
}
|
||||
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.mPassword = password;
|
||||
}
|
||||
|
||||
public String getCertificateAlias()
|
||||
{
|
||||
return mCertificate;
|
||||
}
|
||||
|
||||
public void setCertificateAlias(String certificate)
|
||||
{
|
||||
this.mCertificate = certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (o != null && o instanceof VpnProfile)
|
||||
{
|
||||
return this.mId == ((VpnProfile)o).getId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VpnProfile clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
return (VpnProfile)super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
public class VpnProfileDataSource
|
||||
{
|
||||
private static final String TAG = VpnProfileDataSource.class.getSimpleName();
|
||||
public static final String KEY_ID = "_id";
|
||||
public static final String KEY_NAME = "name";
|
||||
public static final String KEY_GATEWAY = "gateway";
|
||||
public static final String KEY_USERNAME = "username";
|
||||
public static final String KEY_PASSWORD = "password";
|
||||
public static final String KEY_CERTIFICATE = "certificate";
|
||||
|
||||
private DatabaseHelper mDbHelper;
|
||||
private SQLiteDatabase mDatabase;
|
||||
private final Context mContext;
|
||||
|
||||
private static final String DATABASE_NAME = "strongswan.db";
|
||||
private static final String TABLE_VPNPROFILE = "vpnprofile";
|
||||
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
public static final String DATABASE_CREATE =
|
||||
"CREATE TABLE " + TABLE_VPNPROFILE + " (" +
|
||||
KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
KEY_NAME + " TEXT NOT NULL," +
|
||||
KEY_GATEWAY + " TEXT NOT NULL," +
|
||||
KEY_USERNAME + " TEXT NOT NULL," +
|
||||
KEY_PASSWORD + " TEXT," +
|
||||
KEY_CERTIFICATE + " TEXT" +
|
||||
");";
|
||||
private final String[] ALL_COLUMNS = new String[] {
|
||||
KEY_ID,
|
||||
KEY_NAME,
|
||||
KEY_GATEWAY,
|
||||
KEY_USERNAME,
|
||||
KEY_PASSWORD,
|
||||
KEY_CERTIFICATE
|
||||
};
|
||||
|
||||
private static class DatabaseHelper extends SQLiteOpenHelper
|
||||
{
|
||||
public DatabaseHelper(Context context)
|
||||
{
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase database)
|
||||
{
|
||||
database.execSQL(DATABASE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
|
||||
{
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion +
|
||||
" to " + newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_VPNPROFILE);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new VPN profile data source. The context is used to
|
||||
* open/create the database.
|
||||
* @param context context used to access the database
|
||||
*/
|
||||
public VpnProfileDataSource(Context context)
|
||||
{
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the VPN profile data source. The database is automatically created
|
||||
* if it does not yet exist. If that fails an exception is thrown.
|
||||
* @return itself (allows to chain initialization calls)
|
||||
* @throws SQLException if the database could not be opened or created
|
||||
*/
|
||||
public VpnProfileDataSource open() throws SQLException
|
||||
{
|
||||
if (mDbHelper == null)
|
||||
{
|
||||
mDbHelper = new DatabaseHelper(mContext);
|
||||
mDatabase = mDbHelper.getWritableDatabase();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the data source.
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
if (mDbHelper != null)
|
||||
{
|
||||
mDbHelper.close();
|
||||
mDbHelper = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given VPN profile into the database. On success the Id of
|
||||
* the object is updated and the object returned.
|
||||
*
|
||||
* @param profile the profile to add
|
||||
* @return the added VPN profile or null, if failed
|
||||
*/
|
||||
public VpnProfile insertProfile(VpnProfile profile)
|
||||
{
|
||||
ContentValues values = ContentValuesFromVpnProfile(profile);
|
||||
long insertId = mDatabase.insert(TABLE_VPNPROFILE, null, values);
|
||||
if (insertId == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
profile.setId(insertId);
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given VPN profile in the database.
|
||||
* @param profile the profile to update
|
||||
* @return true if update succeeded, false otherwise
|
||||
*/
|
||||
public boolean updateVpnProfile(VpnProfile profile)
|
||||
{
|
||||
long id = profile.getId();
|
||||
ContentValues values = ContentValuesFromVpnProfile(profile);
|
||||
return mDatabase.update(TABLE_VPNPROFILE, values, KEY_ID + " = " + id, null) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the given VPN profile from the database.
|
||||
* @param profile the profile to delete
|
||||
* @return true if deleted, false otherwise
|
||||
*/
|
||||
public boolean deleteVpnProfile(VpnProfile profile)
|
||||
{
|
||||
long id = profile.getId();
|
||||
return mDatabase.delete(TABLE_VPNPROFILE, KEY_ID + " = " + id, null) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single VPN profile from the database.
|
||||
* @param id the ID of the VPN profile
|
||||
* @return the profile or null, if not found
|
||||
*/
|
||||
public VpnProfile getVpnProfile(long id)
|
||||
{
|
||||
VpnProfile profile = null;
|
||||
Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
|
||||
KEY_ID + "=" + id, null, null, null, null);
|
||||
if (cursor.moveToFirst())
|
||||
{
|
||||
profile = VpnProfileFromCursor(cursor);
|
||||
}
|
||||
cursor.close();
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all VPN profiles stored in the database.
|
||||
* @return list of VPN profiles
|
||||
*/
|
||||
public List<VpnProfile> getAllVpnProfiles()
|
||||
{
|
||||
List<VpnProfile> vpnProfiles = new ArrayList<VpnProfile>();
|
||||
|
||||
Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS, null, null, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast())
|
||||
{
|
||||
VpnProfile vpnProfile = VpnProfileFromCursor(cursor);
|
||||
vpnProfiles.add(vpnProfile);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
cursor.close();
|
||||
return vpnProfiles;
|
||||
}
|
||||
|
||||
private VpnProfile VpnProfileFromCursor(Cursor cursor)
|
||||
{
|
||||
VpnProfile profile = new VpnProfile();
|
||||
profile.setId(cursor.getLong(cursor.getColumnIndex(KEY_ID)));
|
||||
profile.setName(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
|
||||
profile.setGateway(cursor.getString(cursor.getColumnIndex(KEY_GATEWAY)));
|
||||
profile.setUsername(cursor.getString(cursor.getColumnIndex(KEY_USERNAME)));
|
||||
profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD)));
|
||||
profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE)));
|
||||
return profile;
|
||||
}
|
||||
|
||||
private ContentValues ContentValuesFromVpnProfile(VpnProfile profile)
|
||||
{
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_NAME, profile.getName());
|
||||
values.put(KEY_GATEWAY, profile.getGateway());
|
||||
values.put(KEY_USERNAME, profile.getUsername());
|
||||
values.put(KEY_PASSWORD, profile.getPassword());
|
||||
values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
|
||||
return values;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.logic.VpnStateService.ErrorState;
|
||||
import org.strongswan.android.logic.VpnStateService.State;
|
||||
import org.strongswan.android.ui.MainActivity;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.net.VpnService;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
public class CharonVpnService extends VpnService implements Runnable
|
||||
{
|
||||
private static final String TAG = CharonVpnService.class.getSimpleName();
|
||||
public static final String LOG_FILE = "charon.log";
|
||||
|
||||
private String mLogFile;
|
||||
private VpnProfileDataSource mDataSource;
|
||||
private Thread mConnectionHandler;
|
||||
private VpnProfile mCurrentProfile;
|
||||
private volatile String mCurrentCertificateAlias;
|
||||
private VpnProfile mNextProfile;
|
||||
private volatile boolean mProfileUpdated;
|
||||
private volatile boolean mTerminate;
|
||||
private volatile boolean mIsDisconnecting;
|
||||
private VpnStateService mService;
|
||||
private final Object mServiceLock = new Object();
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name)
|
||||
{ /* since the service is local this is theoretically only called when the process is terminated */
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
mService = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service)
|
||||
{
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
mService = ((VpnStateService.LocalBinder)service).getService();
|
||||
}
|
||||
/* we are now ready to start the handler thread */
|
||||
mConnectionHandler.start();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* as defined in charonservice.h
|
||||
*/
|
||||
static final int STATE_CHILD_SA_UP = 1;
|
||||
static final int STATE_CHILD_SA_DOWN = 2;
|
||||
static final int STATE_AUTH_ERROR = 3;
|
||||
static final int STATE_PEER_AUTH_ERROR = 4;
|
||||
static final int STATE_LOOKUP_ERROR = 5;
|
||||
static final int STATE_UNREACHABLE_ERROR = 6;
|
||||
static final int STATE_GENERIC_ERROR = 7;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId)
|
||||
{
|
||||
if (intent != null)
|
||||
{
|
||||
Bundle bundle = intent.getExtras();
|
||||
VpnProfile profile = null;
|
||||
if (bundle != null)
|
||||
{
|
||||
profile = mDataSource.getVpnProfile(bundle.getLong(VpnProfileDataSource.KEY_ID));
|
||||
if (profile != null)
|
||||
{
|
||||
String password = bundle.getString(VpnProfileDataSource.KEY_PASSWORD);
|
||||
profile.setPassword(password);
|
||||
}
|
||||
}
|
||||
setNextProfile(profile);
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
mLogFile = getFilesDir().getAbsolutePath() + File.separator + LOG_FILE;
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource.open();
|
||||
/* use a separate thread as main thread for charon */
|
||||
mConnectionHandler = new Thread(this);
|
||||
/* the thread is started when the service is bound */
|
||||
bindService(new Intent(this, VpnStateService.class),
|
||||
mServiceConnection, Service.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevoke()
|
||||
{ /* the system revoked the rights grated with the initial prepare() call.
|
||||
* called when the user clicks disconnect in the system's VPN dialog */
|
||||
setNextProfile(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
mTerminate = true;
|
||||
setNextProfile(null);
|
||||
try
|
||||
{
|
||||
mConnectionHandler.join();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (mService != null)
|
||||
{
|
||||
unbindService(mServiceConnection);
|
||||
}
|
||||
mDataSource.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the profile that is to be initiated next. Notify the handler thread.
|
||||
*
|
||||
* @param profile the profile to initiate
|
||||
*/
|
||||
private void setNextProfile(VpnProfile profile)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
this.mNextProfile = profile;
|
||||
mProfileUpdated = true;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!mProfileUpdated)
|
||||
{
|
||||
wait();
|
||||
}
|
||||
|
||||
mProfileUpdated = false;
|
||||
stopCurrentConnection();
|
||||
if (mNextProfile == null)
|
||||
{
|
||||
setProfile(null);
|
||||
setState(State.DISABLED);
|
||||
if (mTerminate)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentProfile = mNextProfile;
|
||||
mNextProfile = null;
|
||||
|
||||
/* store this in a separate (volatile) variable to avoid
|
||||
* a possible deadlock during deinitialization */
|
||||
mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias();
|
||||
|
||||
setProfile(mCurrentProfile);
|
||||
setError(ErrorState.NO_ERROR);
|
||||
setState(State.CONNECTING);
|
||||
mIsDisconnecting = false;
|
||||
|
||||
BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName());
|
||||
initializeCharon(builder, mLogFile);
|
||||
Log.i(TAG, "charon started");
|
||||
|
||||
String local_address = getLocalIPv4Address();
|
||||
initiate(local_address != null ? local_address : "0.0.0.0",
|
||||
mCurrentProfile.getGateway(), mCurrentProfile.getUsername(),
|
||||
mCurrentProfile.getPassword());
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
stopCurrentConnection();
|
||||
setState(State.DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop any existing connection by deinitializing charon.
|
||||
*/
|
||||
private void stopCurrentConnection()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (mCurrentProfile != null)
|
||||
{
|
||||
setState(State.DISCONNECTING);
|
||||
mIsDisconnecting = true;
|
||||
deinitializeCharon();
|
||||
Log.i(TAG, "charon stopped");
|
||||
mCurrentProfile = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the VPN profile on the state service. Called by the handler thread.
|
||||
*
|
||||
* @param profile currently active VPN profile
|
||||
*/
|
||||
private void setProfile(VpnProfile profile)
|
||||
{
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.setProfile(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current VPN state on the state service. Called by the handler
|
||||
* thread and any of charon's threads.
|
||||
*
|
||||
* @param state current state
|
||||
*/
|
||||
private void setState(State state)
|
||||
{
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.setState(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an error on the state service. Called by the handler thread and any
|
||||
* of charon's threads.
|
||||
*
|
||||
* @param error error state
|
||||
*/
|
||||
private void setError(ErrorState error)
|
||||
{
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.setError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an error on the state service and disconnect the current connection.
|
||||
* This is not done by calling stopCurrentConnection() above, but instead
|
||||
* is done asynchronously via state service.
|
||||
*
|
||||
* @param error error state
|
||||
*/
|
||||
private void setErrorDisconnect(ErrorState error)
|
||||
{
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.setError(error);
|
||||
if (!mIsDisconnecting)
|
||||
{
|
||||
mService.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of the current connection.
|
||||
* Called via JNI by different threads (but not concurrently).
|
||||
*
|
||||
* @param status new state
|
||||
*/
|
||||
public void updateStatus(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case STATE_CHILD_SA_DOWN:
|
||||
synchronized (mServiceLock)
|
||||
{
|
||||
/* if we are not actively disconnecting we assume the remote terminated
|
||||
* the connection and call disconnect() to deinitialize charon properly */
|
||||
if (mService != null && !mIsDisconnecting)
|
||||
{
|
||||
mService.disconnect();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_CHILD_SA_UP:
|
||||
setState(State.CONNECTED);
|
||||
break;
|
||||
case STATE_AUTH_ERROR:
|
||||
setErrorDisconnect(ErrorState.AUTH_FAILED);
|
||||
break;
|
||||
case STATE_PEER_AUTH_ERROR:
|
||||
setErrorDisconnect(ErrorState.PEER_AUTH_FAILED);
|
||||
break;
|
||||
case STATE_LOOKUP_ERROR:
|
||||
setErrorDisconnect(ErrorState.LOOKUP_FAILED);
|
||||
break;
|
||||
case STATE_UNREACHABLE_ERROR:
|
||||
setErrorDisconnect(ErrorState.UNREACHABLE);
|
||||
break;
|
||||
case STATE_GENERIC_ERROR:
|
||||
setErrorDisconnect(ErrorState.GENERIC_ERROR);
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown status code received");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called via JNI to generate a list of DER encoded CA certificates
|
||||
* as byte array.
|
||||
*
|
||||
* @param hash optional alias (only hash part), if given matching certificates are returned
|
||||
* @return a list of DER encoded CA certificates
|
||||
*/
|
||||
private byte[][] getTrustedCertificates(String hash)
|
||||
{
|
||||
ArrayList<byte[]> certs = new ArrayList<byte[]>();
|
||||
TrustedCertificateManager certman = TrustedCertificateManager.getInstance();
|
||||
try
|
||||
{
|
||||
if (hash != null)
|
||||
{
|
||||
String alias = "user:" + hash + ".0";
|
||||
X509Certificate cert = certman.getCACertificateFromAlias(alias);
|
||||
if (cert == null)
|
||||
{
|
||||
alias = "system:" + hash + ".0";
|
||||
cert = certman.getCACertificateFromAlias(alias);
|
||||
}
|
||||
if (cert == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
certs.add(cert.getEncoded());
|
||||
}
|
||||
else
|
||||
{
|
||||
String alias = this.mCurrentCertificateAlias;
|
||||
if (alias != null)
|
||||
{
|
||||
X509Certificate cert = certman.getCACertificateFromAlias(alias);
|
||||
if (cert == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
certs.add(cert.getEncoded());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (X509Certificate cert : certman.getAllCACertificates().values())
|
||||
{
|
||||
certs.add(cert.getEncoded());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CertificateEncodingException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return certs.toArray(new byte[certs.size()][]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of charon, provided by libandroidbridge.so
|
||||
*
|
||||
* @param builder BuilderAdapter for this connection
|
||||
* @param logfile absolute path to the logfile
|
||||
*/
|
||||
public native void initializeCharon(BuilderAdapter builder, String logfile);
|
||||
|
||||
/**
|
||||
* Deinitialize charon, provided by libandroidbridge.so
|
||||
*/
|
||||
public native void deinitializeCharon();
|
||||
|
||||
/**
|
||||
* Initiate VPN, provided by libandroidbridge.so
|
||||
*/
|
||||
public native void initiate(String local_address, String gateway,
|
||||
String username, String password);
|
||||
|
||||
/**
|
||||
* Helper function that retrieves a local IPv4 address.
|
||||
*
|
||||
* @return string representation of an IPv4 address, or null if none found
|
||||
*/
|
||||
private static String getLocalIPv4Address()
|
||||
{
|
||||
try
|
||||
{
|
||||
Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
|
||||
while (en.hasMoreElements())
|
||||
{
|
||||
NetworkInterface intf = en.nextElement();
|
||||
|
||||
Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();
|
||||
while (enumIpAddr.hasMoreElements())
|
||||
{
|
||||
InetAddress inetAddress = enumIpAddr.nextElement();
|
||||
if (!inetAddress.isLoopbackAddress() && inetAddress.getAddress().length == 4)
|
||||
{
|
||||
return inetAddress.getHostAddress().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for VpnService.Builder which is used to access it safely via JNI.
|
||||
* There is a corresponding C object to access it from native code.
|
||||
*/
|
||||
public class BuilderAdapter
|
||||
{
|
||||
VpnService.Builder builder;
|
||||
|
||||
public BuilderAdapter(String name)
|
||||
{
|
||||
builder = new CharonVpnService.Builder();
|
||||
builder.setSession(name);
|
||||
|
||||
/* even though the option displayed in the system dialog says "Configure"
|
||||
* we just use our main Activity */
|
||||
Context context = getApplicationContext();
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
PendingIntent pending = PendingIntent.getActivity(context, 0, intent,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
builder.setConfigureIntent(pending);
|
||||
}
|
||||
|
||||
public synchronized boolean addAddress(String address, int prefixLength)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.addAddress(address, prefixLength);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean addDnsServer(String address)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.addDnsServer(address);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean addRoute(String address, int prefixLength)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.addRoute(address, prefixLength);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean addSearchDomain(String domain)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.addSearchDomain(domain);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean setMtu(int mtu)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.setMtu(mtu);
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized int establish()
|
||||
{
|
||||
ParcelFileDescriptor fd;
|
||||
try
|
||||
{
|
||||
fd = builder.establish();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
if (fd == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return fd.detachFd();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The libraries are extracted to /data/data/org.strongswan.android/...
|
||||
* during installation.
|
||||
*/
|
||||
static
|
||||
{
|
||||
System.loadLibrary("crypto");
|
||||
System.loadLibrary("strongswan");
|
||||
System.loadLibrary("hydra");
|
||||
System.loadLibrary("charon");
|
||||
System.loadLibrary("ipsec");
|
||||
System.loadLibrary("androidbridge");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class TrustedCertificateManager
|
||||
{
|
||||
private static final String TAG = TrustedCertificateManager.class.getSimpleName();
|
||||
private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
|
||||
private Hashtable<String, X509Certificate> mCACerts = new Hashtable<String, X509Certificate>();
|
||||
private boolean mLoaded;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation from other classes.
|
||||
*/
|
||||
private TrustedCertificateManager()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not instantiated until the first call to getInstance()
|
||||
*/
|
||||
private static class Singleton {
|
||||
public static final TrustedCertificateManager mInstance = new TrustedCertificateManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the single instance of the CA certificate manager.
|
||||
* @return CA certificate manager
|
||||
*/
|
||||
public static TrustedCertificateManager getInstance()
|
||||
{
|
||||
return Singleton.mInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a load/reload of the cached CA certificates.
|
||||
* As this takes a while it should be called asynchronously.
|
||||
* @return reference to itself
|
||||
*/
|
||||
public TrustedCertificateManager reload()
|
||||
{
|
||||
Log.d(TAG, "Force reload of cached CA certificates");
|
||||
this.mLock.writeLock().lock();
|
||||
loadCertificates();
|
||||
this.mLock.writeLock().unlock();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the certificates are loaded but does not force a reload.
|
||||
* As this takes a while if the certificates are not loaded yet it should
|
||||
* be called asynchronously.
|
||||
* @return reference to itself
|
||||
*/
|
||||
public TrustedCertificateManager load()
|
||||
{
|
||||
Log.d(TAG, "Ensure cached CA certificates are loaded");
|
||||
this.mLock.writeLock().lock();
|
||||
if (!this.mLoaded)
|
||||
{
|
||||
loadCertificates();
|
||||
}
|
||||
this.mLock.writeLock().unlock();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the CA certificate KeyStore and loads the cached certificates.
|
||||
* The lock must be locked when calling this method.
|
||||
*/
|
||||
private void loadCertificates()
|
||||
{
|
||||
Log.d(TAG, "Load cached CA certificates");
|
||||
try
|
||||
{
|
||||
KeyStore store = KeyStore.getInstance("AndroidCAStore");
|
||||
store.load(null, null);
|
||||
this.mCACerts = fetchCertificates(store);
|
||||
this.mLoaded = true;
|
||||
Log.d(TAG, "Cached CA certificates loaded");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
this.mCACerts = new Hashtable<String, X509Certificate>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all X.509 certificates from the given KeyStore.
|
||||
* @param store KeyStore to load certificates from
|
||||
* @return Hashtable mapping aliases to certificates
|
||||
*/
|
||||
private Hashtable<String, X509Certificate> fetchCertificates(KeyStore store)
|
||||
{
|
||||
Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
|
||||
try
|
||||
{
|
||||
Enumeration<String> aliases = store.aliases();
|
||||
while (aliases.hasMoreElements())
|
||||
{
|
||||
String alias = aliases.nextElement();
|
||||
Certificate cert;
|
||||
cert = store.getCertificate(alias);
|
||||
if (cert != null && cert instanceof X509Certificate)
|
||||
{
|
||||
certs.put(alias, (X509Certificate)cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (KeyStoreException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the CA certificate with the given alias.
|
||||
* @param alias alias of the certificate to get
|
||||
* @return the certificate, null if not found
|
||||
*/
|
||||
public X509Certificate getCACertificateFromAlias(String alias)
|
||||
{
|
||||
X509Certificate certificate = null;
|
||||
|
||||
if (this.mLock.readLock().tryLock())
|
||||
{
|
||||
certificate = this.mCACerts.get(alias);
|
||||
this.mLock.readLock().unlock();
|
||||
}
|
||||
else
|
||||
{ /* if we cannot get the lock load it directly from the KeyStore,
|
||||
* should be fast for a single certificate */
|
||||
try
|
||||
{
|
||||
KeyStore store = KeyStore.getInstance("AndroidCAStore");
|
||||
store.load(null, null);
|
||||
Certificate cert = store.getCertificate(alias);
|
||||
if (cert != null && cert instanceof X509Certificate)
|
||||
{
|
||||
certificate = (X509Certificate)cert;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
return certificate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all CA certificates (from the system and user keystore).
|
||||
* @return Hashtable mapping aliases to certificates
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Hashtable<String, X509Certificate> getAllCACertificates()
|
||||
{
|
||||
Hashtable<String, X509Certificate> certs;
|
||||
this.mLock.readLock().lock();
|
||||
certs = (Hashtable<String, X509Certificate>)this.mCACerts.clone();
|
||||
this.mLock.readLock().unlock();
|
||||
return certs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only the CA certificates installed by the user.
|
||||
* @return Hashtable mapping aliases to certificates
|
||||
*/
|
||||
public Hashtable<String, X509Certificate> getUserCACertificates()
|
||||
{
|
||||
Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
|
||||
this.mLock.readLock().lock();
|
||||
for (String alias : this.mCACerts.keySet())
|
||||
{
|
||||
if (alias.startsWith("user:"))
|
||||
{
|
||||
certs.put(alias, this.mCACerts.get(alias));
|
||||
}
|
||||
}
|
||||
this.mLock.readLock().unlock();
|
||||
return certs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.logic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class VpnStateService extends Service
|
||||
{
|
||||
private final List<VpnStateListener> mListeners = new ArrayList<VpnStateListener>();
|
||||
private final IBinder mBinder = new LocalBinder();
|
||||
private Handler mHandler;
|
||||
private VpnProfile mProfile;
|
||||
private State mState = State.DISABLED;
|
||||
private ErrorState mError = ErrorState.NO_ERROR;
|
||||
|
||||
public enum State
|
||||
{
|
||||
DISABLED,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
DISCONNECTING,
|
||||
}
|
||||
|
||||
public enum ErrorState
|
||||
{
|
||||
NO_ERROR,
|
||||
AUTH_FAILED,
|
||||
PEER_AUTH_FAILED,
|
||||
LOOKUP_FAILED,
|
||||
UNREACHABLE,
|
||||
GENERIC_ERROR,
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener interface for bound clients that are interested in changes to
|
||||
* this Service.
|
||||
*/
|
||||
public interface VpnStateListener
|
||||
{
|
||||
public void stateChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Binder that allows to directly access this Service class itself
|
||||
* after binding to it.
|
||||
*/
|
||||
public class LocalBinder extends Binder
|
||||
{
|
||||
public VpnStateService getService()
|
||||
{
|
||||
return VpnStateService.this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
/* this handler allows us to notify listeners from the UI thread and
|
||||
* not from the threads that actually report any state changes */
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent)
|
||||
{
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener with this Service. We assume this is called from
|
||||
* the main thread so no synchronization is happening.
|
||||
*
|
||||
* @param listener listener to register
|
||||
*/
|
||||
public void registerListener(VpnStateListener listener)
|
||||
{
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a listener from this Service.
|
||||
*
|
||||
* @param listener listener to unregister
|
||||
*/
|
||||
public void unregisterListener(VpnStateListener listener)
|
||||
{
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current VPN profile.
|
||||
*
|
||||
* @return profile
|
||||
*/
|
||||
public VpnProfile getProfile()
|
||||
{ /* only updated from the main thread so no synchronization needed */
|
||||
return mProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state.
|
||||
*
|
||||
* @return state
|
||||
*/
|
||||
public State getState()
|
||||
{ /* only updated from the main thread so no synchronization needed */
|
||||
return mState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current error, if any.
|
||||
*
|
||||
* @return error
|
||||
*/
|
||||
public ErrorState getErrorState()
|
||||
{ /* only updated from the main thread so no synchronization needed */
|
||||
return mError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect any existing connection and shutdown the daemon, the
|
||||
* VpnService is not stopped but it is reset so new connections can be
|
||||
* started.
|
||||
*/
|
||||
public void disconnect()
|
||||
{
|
||||
/* as soon as the TUN device is created by calling establish() on the
|
||||
* VpnService.Builder object the system binds to the service and keeps
|
||||
* bound until the file descriptor of the TUN device is closed. thus
|
||||
* calling stopService() here would not stop (destroy) the service yet,
|
||||
* instead we call startService() with an empty Intent which shuts down
|
||||
* the daemon (and closes the TUN device, if any) */
|
||||
Context context = getApplicationContext();
|
||||
Intent intent = new Intent(context, CharonVpnService.class);
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state and notify all listeners about the change. By using a Handler
|
||||
* this is done from the main UI thread and not the initial reporter thread.
|
||||
* Also, in doing the actual state change from the main thread, listeners
|
||||
* see all changes and none are skipped.
|
||||
*
|
||||
* @param change the state update to perform before notifying listeners, returns true if state changed
|
||||
*/
|
||||
private void notifyListeners(final Callable<Boolean> change)
|
||||
{
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (change.call())
|
||||
{ /* otherwise there is no need to notify the listeners */
|
||||
for (VpnStateListener listener : mListeners)
|
||||
{
|
||||
listener.stateChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the VPN profile currently active. Listeners are not notified.
|
||||
*
|
||||
* May be called from threads other than the main thread.
|
||||
*
|
||||
* @param profile current profile
|
||||
*/
|
||||
public void setProfile(final VpnProfile profile)
|
||||
{
|
||||
/* even though we don't notify the listeners the update is done from the
|
||||
* same handler so updates are predictable for listeners */
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
VpnStateService.this.mProfile = profile;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state and notify all listeners, if changed.
|
||||
*
|
||||
* May be called from threads other than the main thread.
|
||||
*
|
||||
* @param state new state
|
||||
*/
|
||||
public void setState(final State state)
|
||||
{
|
||||
notifyListeners(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception
|
||||
{
|
||||
if (VpnStateService.this.mState != state)
|
||||
{
|
||||
VpnStateService.this.mState = state;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current error state and notify all listeners, if changed.
|
||||
*
|
||||
* May be called from threads other than the main thread.
|
||||
*
|
||||
* @param error error state
|
||||
*/
|
||||
public void setError(final ErrorState error)
|
||||
{
|
||||
notifyListeners(new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception
|
||||
{
|
||||
if (VpnStateService.this.mError != error)
|
||||
{
|
||||
VpnStateService.this.mError = error;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package org.strongswan.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.VpnService;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class strongSwanActivity extends Activity
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.main);
|
||||
startVpnService();
|
||||
}
|
||||
|
||||
private void startVpnService()
|
||||
{
|
||||
Intent intent = VpnService.prepare(this);
|
||||
if (intent != null)
|
||||
{
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
onActivityResult(0, RESULT_OK, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
Intent intent = new Intent(this, CharonVpnService.class);
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.LogContentProvider;
|
||||
import org.strongswan.android.logic.CharonVpnService;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class LogActivity extends Activity
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.log_activity);
|
||||
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
getMenuInflater().inflate(R.menu.log, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
case R.id.menu_send_log:
|
||||
File logfile = new File(getFilesDir(), CharonVpnService.LOG_FILE);
|
||||
if (!logfile.exists() || logfile.length() == 0)
|
||||
{
|
||||
Toast.makeText(this, getString(R.string.empty_log), Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
String version = "";
|
||||
try
|
||||
{
|
||||
version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
|
||||
}
|
||||
catch (NameNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { MainActivity.CONTACT_EMAIL });
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, String.format(getString(R.string.log_mail_subject), version));
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, LogContentProvider.createContentUri());
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.send_log)));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.logic.CharonVpnService;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.os.FileObserver;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class LogFragment extends Fragment implements Runnable
|
||||
{
|
||||
private String mLogFilePath;
|
||||
private Handler mLogHandler;
|
||||
private TextView mLogView;
|
||||
private LogScrollView mScrollView;
|
||||
private BufferedReader mReader;
|
||||
private Thread mThread;
|
||||
private volatile boolean mRunning;
|
||||
private FileObserver mDirectoryObserver;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mLogFilePath = getActivity().getFilesDir() + File.separator + CharonVpnService.LOG_FILE;
|
||||
/* use a handler to update the log view */
|
||||
mLogHandler = new Handler();
|
||||
|
||||
mDirectoryObserver = new LogDirectoryObserver(getActivity().getFilesDir().getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.log_fragment, null);
|
||||
mLogView = (TextView)view.findViewById(R.id.log_view);
|
||||
mScrollView = (LogScrollView)view.findViewById(R.id.scroll_view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart()
|
||||
{
|
||||
super.onStart();
|
||||
startLogReader();
|
||||
mDirectoryObserver.startWatching();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
mDirectoryObserver.stopWatching();
|
||||
stopLogReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start reading from the log file
|
||||
*/
|
||||
private void startLogReader()
|
||||
{
|
||||
try
|
||||
{
|
||||
mReader = new BufferedReader(new FileReader(mLogFilePath));
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
mReader = new BufferedReader(new StringReader(""));
|
||||
}
|
||||
|
||||
mLogView.setText("");
|
||||
mRunning = true;
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop reading from the log file
|
||||
*/
|
||||
private void stopLogReader()
|
||||
{
|
||||
try
|
||||
{
|
||||
mRunning = false;
|
||||
mThread.interrupt();
|
||||
mThread.join();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given log line to the TextView. We strip the prefix off to save
|
||||
* some space (it is not that helpful for regular users anyway).
|
||||
*
|
||||
* @param line log line to log
|
||||
*/
|
||||
public void logLine(final String line)
|
||||
{
|
||||
mLogHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
/* strip off prefix (month=3, day=2, time=8, thread=2, spaces=3) */
|
||||
mLogView.append((line.length() > 18 ? line.substring(18) : line) + '\n');
|
||||
/* calling autoScroll() directly does not work, probably because content
|
||||
* is not yet updated, so we post this to be done later */
|
||||
mScrollView.post(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mScrollView.autoScroll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (mRunning)
|
||||
{
|
||||
try
|
||||
{ /* this works as long as the file is not truncated */
|
||||
String line = mReader.readLine();
|
||||
if (line == null)
|
||||
{ /* wait until there is more to log */
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
logLine(line);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FileObserver that checks for changes regarding the log file. Since charon
|
||||
* truncates it (for which there is no explicit event) we check for any modification
|
||||
* to the file, keep track of the file size and reopen it if it got smaller.
|
||||
*/
|
||||
private class LogDirectoryObserver extends FileObserver
|
||||
{
|
||||
private final File mFile;
|
||||
private long mSize;
|
||||
|
||||
public LogDirectoryObserver(String path)
|
||||
{
|
||||
super(path, FileObserver.CREATE | FileObserver.MODIFY | FileObserver.DELETE);
|
||||
mFile = new File(mLogFilePath);
|
||||
mSize = mFile.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int event, String path)
|
||||
{
|
||||
if (path == null || !path.equals(CharonVpnService.LOG_FILE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (event)
|
||||
{ /* even though we only subscribed for these we check them,
|
||||
* as strange events are sometimes received */
|
||||
case FileObserver.CREATE:
|
||||
case FileObserver.DELETE:
|
||||
restartLogReader();
|
||||
break;
|
||||
case FileObserver.MODIFY:
|
||||
/* if the size got smaller reopen the log file, as it was probably truncated */
|
||||
long size = mFile.length();
|
||||
if (size < mSize)
|
||||
{
|
||||
restartLogReader();
|
||||
}
|
||||
mSize = size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void restartLogReader()
|
||||
{
|
||||
/* we are called from a separate thread, so we use the handler */
|
||||
mLogHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
stopLogReader();
|
||||
startLogReader();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
public class LogScrollView extends ScrollView
|
||||
{
|
||||
private boolean mAutoScroll = true;
|
||||
|
||||
public LogScrollView(Context context)
|
||||
{
|
||||
super(context);
|
||||
}
|
||||
|
||||
public LogScrollView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public LogScrollView(Context context, AttributeSet attrs, int defStyle)
|
||||
{
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev)
|
||||
{
|
||||
/* disable auto-scrolling when the user starts scrolling around */
|
||||
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
mAutoScroll = false;
|
||||
}
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to move newly added content into view by scrolling to the bottom.
|
||||
* Nothing happens if auto-scrolling is disabled.
|
||||
*/
|
||||
public void autoScroll()
|
||||
{
|
||||
if (mAutoScroll)
|
||||
{
|
||||
fullScroll(View.FOCUS_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScrollChanged(int l, int t, int oldl, int oldt)
|
||||
{
|
||||
super.onScrollChanged(l, t, oldl, oldt);
|
||||
/* if the user scrolls to the bottom we enable auto-scrolling again */
|
||||
if (t == getChildAt(getChildCount() - 1).getHeight() - getHeight())
|
||||
{
|
||||
mAutoScroll = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.logic.CharonVpnService;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
import org.strongswan.android.ui.VpnProfileListFragment.OnVpnProfileSelectedListener;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.VpnService;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.EditText;
|
||||
|
||||
public class MainActivity extends Activity implements OnVpnProfileSelectedListener
|
||||
{
|
||||
public static final String CONTACT_EMAIL = "android@strongswan.org";
|
||||
private static final int PREPARE_VPN_SERVICE = 0;
|
||||
private VpnProfile activeProfile;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
setContentView(R.layout.main);
|
||||
|
||||
ActionBar bar = getActionBar();
|
||||
bar.setDisplayShowTitleEnabled(false);
|
||||
|
||||
/* load CA certificates in a background task */
|
||||
new CertificateLoadTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
getMenuInflater().inflate(R.menu.main, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.menu_reload_certs:
|
||||
new CertificateLoadTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, true);
|
||||
return true;
|
||||
case R.id.menu_show_log:
|
||||
Intent logIntent = new Intent(this, LogActivity.class);
|
||||
startActivity(logIntent);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the VpnService. If this succeeds the current VPN profile is
|
||||
* started.
|
||||
*/
|
||||
protected void prepareVpnService()
|
||||
{
|
||||
Intent intent = VpnService.prepare(this);
|
||||
if (intent != null)
|
||||
{
|
||||
startActivityForResult(intent, PREPARE_VPN_SERVICE);
|
||||
}
|
||||
else
|
||||
{
|
||||
onActivityResult(PREPARE_VPN_SERVICE, RESULT_OK, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case PREPARE_VPN_SERVICE:
|
||||
if (resultCode == RESULT_OK && activeProfile != null)
|
||||
{
|
||||
Intent intent = new Intent(this, CharonVpnService.class);
|
||||
intent.putExtra(VpnProfileDataSource.KEY_ID, activeProfile.getId());
|
||||
/* submit the password as the profile might not store one */
|
||||
intent.putExtra(VpnProfileDataSource.KEY_PASSWORD, activeProfile.getPassword());
|
||||
this.startService(intent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVpnProfileSelected(VpnProfile profile)
|
||||
{
|
||||
activeProfile = profile;
|
||||
if (activeProfile.getPassword() == null)
|
||||
{
|
||||
new LoginDialog().show(getFragmentManager(), "LoginDialog");
|
||||
}
|
||||
else
|
||||
{
|
||||
prepareVpnService();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that loads or reloads the cached CA certificates.
|
||||
*/
|
||||
private class CertificateLoadTask extends AsyncTask<Boolean, Void, TrustedCertificateManager>
|
||||
{
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
}
|
||||
@Override
|
||||
protected TrustedCertificateManager doInBackground(Boolean... params)
|
||||
{
|
||||
if (params.length > 0 && params[0])
|
||||
{ /* force a reload of the certificates */
|
||||
return TrustedCertificateManager.getInstance().reload();
|
||||
}
|
||||
return TrustedCertificateManager.getInstance().load();
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(TrustedCertificateManager result)
|
||||
{
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
private class LoginDialog extends DialogFragment
|
||||
{
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState)
|
||||
{
|
||||
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View view = inflater.inflate(R.layout.login_dialog, null);
|
||||
EditText username = (EditText)view.findViewById(R.id.username);
|
||||
username.setText(activeProfile.getUsername());
|
||||
final EditText password = (EditText)view.findViewById(R.id.password);
|
||||
|
||||
Builder adb = new AlertDialog.Builder(MainActivity.this);
|
||||
adb.setView(view);
|
||||
adb.setTitle(getString(R.string.login_title));
|
||||
adb.setPositiveButton(R.string.login_confirm, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int whichButton)
|
||||
{
|
||||
/* let's work on a clone of the profile when updating the password */
|
||||
activeProfile = activeProfile.clone();
|
||||
activeProfile.setPassword(password.getText().toString().trim());
|
||||
prepareVpnService();
|
||||
}
|
||||
});
|
||||
adb.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
return adb.create();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.logic.TrustedCertificateManager;
|
||||
import org.strongswan.android.ui.adapter.TrustedCertificateAdapter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
|
||||
public class VpnProfileDetailActivity extends Activity
|
||||
{
|
||||
private VpnProfileDataSource mDataSource;
|
||||
private Long mId;
|
||||
private VpnProfile mProfile;
|
||||
private boolean mCertsLoaded;
|
||||
private String mCertAlias;
|
||||
private Spinner mCertSpinner;
|
||||
private TrustedCertificateAdapter.CertEntry mSelectedCert;
|
||||
private EditText mName;
|
||||
private EditText mGateway;
|
||||
private EditText mUsername;
|
||||
private EditText mPassword;
|
||||
private CheckBox mCheckAll;
|
||||
private CheckBox mCheckAuto;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
|
||||
/* the title is set when we load the profile, if any */
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this);
|
||||
mDataSource.open();
|
||||
|
||||
setContentView(R.layout.profile_detail_view);
|
||||
|
||||
mName = (EditText)findViewById(R.id.name);
|
||||
mPassword = (EditText)findViewById(R.id.password);
|
||||
mGateway = (EditText)findViewById(R.id.gateway);
|
||||
mUsername = (EditText)findViewById(R.id.username);
|
||||
|
||||
mCheckAll = (CheckBox)findViewById(R.id.ca_show_all);
|
||||
mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
|
||||
mCertSpinner = (Spinner)findViewById(R.id.ca_spinner);
|
||||
|
||||
mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
|
||||
{
|
||||
updateCertSpinner();
|
||||
}
|
||||
});
|
||||
|
||||
mCheckAll.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
|
||||
{
|
||||
Hashtable<String, X509Certificate> certs;
|
||||
certs = isChecked ? TrustedCertificateManager.getInstance().getAllCACertificates()
|
||||
: TrustedCertificateManager.getInstance().getUserCACertificates();
|
||||
mCertSpinner.setAdapter(new TrustedCertificateAdapter(VpnProfileDetailActivity.this, certs));
|
||||
mSelectedCert = (TrustedCertificateAdapter.CertEntry)mCertSpinner.getSelectedItem();
|
||||
}
|
||||
});
|
||||
|
||||
mCertSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view,
|
||||
int pos, long id)
|
||||
{
|
||||
mSelectedCert = (TrustedCertificateAdapter.CertEntry)parent.getSelectedItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> arg0)
|
||||
{
|
||||
mSelectedCert = null;
|
||||
}
|
||||
});
|
||||
|
||||
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
|
||||
if (mId == null)
|
||||
{
|
||||
Bundle extras = getIntent().getExtras();
|
||||
mId = extras == null ? null : extras.getLong(VpnProfileDataSource.KEY_ID);
|
||||
}
|
||||
|
||||
loadProfileData();
|
||||
|
||||
new CertificateLoadTask().execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
mDataSource.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState)
|
||||
{
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putLong(VpnProfileDataSource.KEY_ID, mId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.profile_edit, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
case R.id.menu_cancel:
|
||||
finish();
|
||||
return true;
|
||||
case R.id.menu_accept:
|
||||
saveProfile();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an alert in case the previously selected certificate is not found anymore
|
||||
* or the user did not select a certificate in the spinner.
|
||||
*/
|
||||
private void showCertificateAlert()
|
||||
{
|
||||
AlertDialog.Builder adb = new AlertDialog.Builder(VpnProfileDetailActivity.this);
|
||||
adb.setTitle(R.string.alert_text_nocertfound_title);
|
||||
adb.setMessage(R.string.alert_text_nocertfound);
|
||||
adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id)
|
||||
{
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
adb.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously executed task which confirms that the certificates are loaded.
|
||||
* They are loaded from the main Activity already but might not be ready yet, or
|
||||
* unloaded again.
|
||||
*
|
||||
* Once loaded the CA certificate spinner and checkboxes are updated
|
||||
* accordingly.
|
||||
*/
|
||||
private class CertificateLoadTask extends AsyncTask<Void, Void, TrustedCertificateManager>
|
||||
{
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TrustedCertificateManager doInBackground(Void... params)
|
||||
{
|
||||
return TrustedCertificateManager.getInstance().load();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(TrustedCertificateManager result)
|
||||
{
|
||||
TrustedCertificateAdapter adapter;
|
||||
if (mCertAlias != null && mCertAlias.startsWith("system:"))
|
||||
{
|
||||
mCheckAll.setChecked(true);
|
||||
adapter = new TrustedCertificateAdapter(VpnProfileDetailActivity.this,
|
||||
result.getAllCACertificates());
|
||||
}
|
||||
else
|
||||
{
|
||||
mCheckAll.setChecked(false);
|
||||
adapter = new TrustedCertificateAdapter(VpnProfileDetailActivity.this,
|
||||
result.getUserCACertificates());
|
||||
}
|
||||
mCertSpinner.setAdapter(adapter);
|
||||
|
||||
if (mCertAlias != null)
|
||||
{
|
||||
int position = adapter.getItemPosition(mCertAlias);
|
||||
if (position == -1)
|
||||
{ /* previously selected certificate is not here anymore */
|
||||
showCertificateAlert();
|
||||
}
|
||||
else
|
||||
{
|
||||
mCertSpinner.setSelection(position);
|
||||
}
|
||||
}
|
||||
|
||||
mSelectedCert = (TrustedCertificateAdapter.CertEntry)mCertSpinner.getSelectedItem();
|
||||
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
mCertsLoaded = true;
|
||||
updateCertSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the CA certificate selection UI depending on whether the
|
||||
* certificate should be automatically selected or not.
|
||||
*/
|
||||
private void updateCertSpinner()
|
||||
{
|
||||
if (!mCheckAuto.isChecked())
|
||||
{
|
||||
if (mCertsLoaded)
|
||||
{
|
||||
mCertSpinner.setEnabled(true);
|
||||
mCertSpinner.setVisibility(View.VISIBLE);
|
||||
mCheckAll.setEnabled(true);
|
||||
mCheckAll.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mCertSpinner.setEnabled(false);
|
||||
mCertSpinner.setVisibility(View.GONE);
|
||||
mCheckAll.setEnabled(false);
|
||||
mCheckAll.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update the profile depending on whether we actually have a
|
||||
* profile object or not (this was created in updateProfileData)
|
||||
*/
|
||||
private void saveProfile()
|
||||
{
|
||||
if (verifyInput())
|
||||
{
|
||||
if (mProfile != null)
|
||||
{
|
||||
updateProfileData();
|
||||
mDataSource.updateVpnProfile(mProfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
mProfile = new VpnProfile();
|
||||
updateProfileData();
|
||||
mDataSource.insertProfile(mProfile);
|
||||
}
|
||||
setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_ID, mProfile.getId()));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the user input and display error messages.
|
||||
* @return true if the input is valid
|
||||
*/
|
||||
private boolean verifyInput()
|
||||
{
|
||||
boolean valid = true;
|
||||
if (mGateway.getText().toString().trim().isEmpty())
|
||||
{
|
||||
mGateway.setError(getString(R.string.alert_text_no_input_gateway));
|
||||
valid = false;
|
||||
}
|
||||
if (mUsername.getText().toString().trim().isEmpty())
|
||||
{
|
||||
mUsername.setError(getString(R.string.alert_text_no_input_username));
|
||||
valid = false;
|
||||
}
|
||||
if (!mCheckAuto.isChecked() && mSelectedCert == null)
|
||||
{
|
||||
showCertificateAlert();
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the profile object with the data entered by the user
|
||||
*/
|
||||
private void updateProfileData()
|
||||
{
|
||||
/* the name is optional, we default to the gateway if none is given */
|
||||
String name = mName.getText().toString().trim();
|
||||
String gateway = mGateway.getText().toString().trim();
|
||||
mProfile.setName(name.isEmpty() ? gateway : name);
|
||||
mProfile.setGateway(gateway);
|
||||
mProfile.setUsername(mUsername.getText().toString().trim());
|
||||
String password = mPassword.getText().toString().trim();
|
||||
password = password.isEmpty() ? null : password;
|
||||
mProfile.setPassword(password);
|
||||
String certAlias = mCheckAuto.isChecked() ? null : mSelectedCert.mAlias;
|
||||
mProfile.setCertificateAlias(certAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an existing profile if we got an ID
|
||||
*/
|
||||
private void loadProfileData()
|
||||
{
|
||||
getActionBar().setTitle(R.string.add_profile);
|
||||
if (mId != null)
|
||||
{
|
||||
mProfile = mDataSource.getVpnProfile(mId);
|
||||
if (mProfile != null)
|
||||
{
|
||||
mName.setText(mProfile.getName());
|
||||
mGateway.setText(mProfile.getGateway());
|
||||
mUsername.setText(mProfile.getUsername());
|
||||
mPassword.setText(mProfile.getPassword());
|
||||
mCertAlias = mProfile.getCertificateAlias();
|
||||
getActionBar().setTitle(mProfile.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e(VpnProfileDetailActivity.class.getSimpleName(),
|
||||
"VPN profile with id " + mId + " not found");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
mCheckAll.setChecked(false);
|
||||
mCheckAuto.setChecked(mCertAlias == null);
|
||||
updateCertSpinner();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.data.VpnProfileDataSource;
|
||||
import org.strongswan.android.ui.adapter.VpnProfileAdapter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.ActionMode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class VpnProfileListFragment extends Fragment
|
||||
{
|
||||
private static final int ADD_REQUEST = 1;
|
||||
private static final int EDIT_REQUEST = 2;
|
||||
|
||||
private List<VpnProfile> mVpnProfiles;
|
||||
private VpnProfileDataSource mDataSource;
|
||||
private VpnProfileAdapter mListAdapter;
|
||||
private ListView mListView;
|
||||
private OnVpnProfileSelectedListener mListener;
|
||||
|
||||
/**
|
||||
* The activity containing this fragment should implement this interface
|
||||
*/
|
||||
public interface OnVpnProfileSelectedListener {
|
||||
public void onVpnProfileSelected(VpnProfile profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.profile_list_fragment, null);
|
||||
|
||||
mListView = (ListView)view.findViewById(R.id.profile_list);
|
||||
mListView.setEmptyView(view.findViewById(R.id.profile_list_empty));
|
||||
mListView.setOnItemClickListener(mVpnProfileClicked);
|
||||
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
mListView.setMultiChoiceModeListener(mVpnProfileSelected);
|
||||
mListView.setAdapter(mListAdapter);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
Context context = getActivity().getApplicationContext();
|
||||
|
||||
mDataSource = new VpnProfileDataSource(this.getActivity());
|
||||
mDataSource.open();
|
||||
|
||||
/* cached list of profiles used as backend for the ListView */
|
||||
mVpnProfiles = mDataSource.getAllVpnProfiles();
|
||||
|
||||
mListAdapter = new VpnProfileAdapter(context, R.layout.profile_list_item, mVpnProfiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
mDataSource.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity)
|
||||
{
|
||||
super.onAttach(activity);
|
||||
|
||||
if (activity instanceof OnVpnProfileSelectedListener)
|
||||
{
|
||||
mListener = (OnVpnProfileSelectedListener)activity;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
||||
{
|
||||
inflater.inflate(R.menu.profile_list, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.add_profile:
|
||||
Intent connectionIntent = new Intent(getActivity(),
|
||||
VpnProfileDetailActivity.class);
|
||||
startActivityForResult(connectionIntent, ADD_REQUEST);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case ADD_REQUEST:
|
||||
case EDIT_REQUEST:
|
||||
if (resultCode != Activity.RESULT_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
long id = data.getLongExtra(VpnProfileDataSource.KEY_ID, 0);
|
||||
VpnProfile profile = mDataSource.getVpnProfile(id);
|
||||
if (profile != null)
|
||||
{ /* in case this was an edit, we remove it first */
|
||||
mVpnProfiles.remove(profile);
|
||||
mVpnProfiles.add(profile);
|
||||
mListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
private final OnItemClickListener mVpnProfileClicked = new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> a, View v, int position, long id)
|
||||
{
|
||||
if (mListener != null)
|
||||
{
|
||||
mListener.onVpnProfileSelected((VpnProfile)a.getItemAtPosition(position));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final MultiChoiceModeListener mVpnProfileSelected = new MultiChoiceModeListener() {
|
||||
private HashSet<Integer> mSelected;
|
||||
private MenuItem mEditProfile;
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
MenuInflater inflater = mode.getMenuInflater();
|
||||
inflater.inflate(R.menu.profile_list_context, menu);
|
||||
mEditProfile = menu.findItem(R.id.edit_profile);
|
||||
mSelected = new HashSet<Integer>();
|
||||
mode.setTitle(R.string.select_profiles);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.edit_profile:
|
||||
{
|
||||
int position = mSelected.iterator().next();
|
||||
VpnProfile profile = (VpnProfile)mListView.getItemAtPosition(position);
|
||||
Intent connectionIntent = new Intent(getActivity(), VpnProfileDetailActivity.class);
|
||||
connectionIntent.putExtra(VpnProfileDataSource.KEY_ID, profile.getId());
|
||||
startActivityForResult(connectionIntent, EDIT_REQUEST);
|
||||
break;
|
||||
}
|
||||
case R.id.delete_profile:
|
||||
{
|
||||
ArrayList<VpnProfile> profiles = new ArrayList<VpnProfile>();
|
||||
for (int position : mSelected)
|
||||
{
|
||||
profiles.add((VpnProfile)mListView.getItemAtPosition(position));
|
||||
}
|
||||
for (VpnProfile profile : profiles)
|
||||
{
|
||||
mDataSource.deleteVpnProfile(profile);
|
||||
mVpnProfiles.remove(profile);
|
||||
}
|
||||
mListAdapter.notifyDataSetChanged();
|
||||
Toast.makeText(VpnProfileListFragment.this.getActivity(),
|
||||
R.string.profiles_deleted, Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position,
|
||||
long id, boolean checked)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
mSelected.add(position);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSelected.remove(position);
|
||||
}
|
||||
final int checkedCount = mSelected.size();
|
||||
mEditProfile.setEnabled(checkedCount == 1);
|
||||
switch (checkedCount)
|
||||
{
|
||||
case 0:
|
||||
mode.setSubtitle(R.string.no_profile_selected);
|
||||
break;
|
||||
case 1:
|
||||
mode.setSubtitle(R.string.one_profile_selected);
|
||||
break;
|
||||
default:
|
||||
mode.setSubtitle(String.format(getString(R.string.x_profiles_selected), checkedCount));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
import org.strongswan.android.logic.VpnStateService;
|
||||
import org.strongswan.android.logic.VpnStateService.ErrorState;
|
||||
import org.strongswan.android.logic.VpnStateService.State;
|
||||
import org.strongswan.android.logic.VpnStateService.VpnStateListener;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class VpnStateFragment extends Fragment implements VpnStateListener
|
||||
{
|
||||
private static final String KEY_ERROR = "error";
|
||||
private static final String KEY_NAME = "name";
|
||||
|
||||
private TextView mProfileNameView;
|
||||
private TextView mProfileView;
|
||||
private TextView mStateView;
|
||||
private int stateBaseColor;
|
||||
private Button mActionButton;
|
||||
private ProgressDialog mProgressDialog;
|
||||
private State mState;
|
||||
private AlertDialog mErrorDialog;
|
||||
private ErrorState mError;
|
||||
private String mErrorProfileName;
|
||||
private VpnStateService mService;
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name)
|
||||
{
|
||||
mService = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service)
|
||||
{
|
||||
mService = ((VpnStateService.LocalBinder)service).getService();
|
||||
mService.registerListener(VpnStateFragment.this);
|
||||
updateView();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
/* bind to the service only seems to work from the ApplicationContext */
|
||||
Context context = getActivity().getApplicationContext();
|
||||
context.bindService(new Intent(context, VpnStateService.class),
|
||||
mServiceConnection, Service.BIND_AUTO_CREATE);
|
||||
|
||||
mError = ErrorState.NO_ERROR;
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ERROR))
|
||||
{
|
||||
mError = (ErrorState)savedInstanceState.getSerializable(KEY_ERROR);
|
||||
mErrorProfileName = savedInstanceState.getString(KEY_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState)
|
||||
{
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putSerializable(KEY_ERROR, mError);
|
||||
outState.putString(KEY_NAME, mErrorProfileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.vpn_state_fragment, null);
|
||||
|
||||
mActionButton = (Button)view.findViewById(R.id.action);
|
||||
mActionButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.disconnect();
|
||||
}
|
||||
}
|
||||
});
|
||||
enableActionButton(false);
|
||||
|
||||
mStateView = (TextView)view.findViewById(R.id.vpn_state);
|
||||
stateBaseColor = mStateView.getCurrentTextColor();
|
||||
mProfileView = (TextView)view.findViewById(R.id.vpn_profile_label);
|
||||
mProfileNameView = (TextView)view.findViewById(R.id.vpn_profile_name);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart()
|
||||
{
|
||||
super.onStart();
|
||||
if (mService != null)
|
||||
{
|
||||
updateView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
hideErrorDialog();
|
||||
hideProgressDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
if (mService != null)
|
||||
{
|
||||
mService.unregisterListener(this);
|
||||
getActivity().getApplicationContext().unbindService(mServiceConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged()
|
||||
{
|
||||
updateView();
|
||||
}
|
||||
|
||||
public void updateView()
|
||||
{
|
||||
State state = mService.getState();
|
||||
ErrorState error = ErrorState.NO_ERROR;
|
||||
String name = "", gateway = "";
|
||||
|
||||
if (state != State.DISABLED)
|
||||
{
|
||||
VpnProfile profile = mService.getProfile();
|
||||
if (profile != null)
|
||||
{
|
||||
name = profile.getName();
|
||||
gateway = profile.getGateway();
|
||||
}
|
||||
error = mService.getErrorState();
|
||||
}
|
||||
|
||||
if (reportError(name, state, error))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == mState)
|
||||
{ /* avoid unnecessary updates */
|
||||
return;
|
||||
}
|
||||
|
||||
hideProgressDialog();
|
||||
enableActionButton(false);
|
||||
mProfileNameView.setText(name);
|
||||
mState = state;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case DISABLED:
|
||||
showProfile(false);
|
||||
mStateView.setText(R.string.state_disabled);
|
||||
mStateView.setTextColor(stateBaseColor);
|
||||
break;
|
||||
case CONNECTING:
|
||||
showProfile(true);
|
||||
showConnectDialog(name, gateway);
|
||||
mStateView.setText(R.string.state_connecting);
|
||||
mStateView.setTextColor(stateBaseColor);
|
||||
break;
|
||||
case CONNECTED:
|
||||
showProfile(true);
|
||||
enableActionButton(true);
|
||||
mStateView.setText(R.string.state_connected);
|
||||
mStateView.setTextColor(getResources().getColor(R.color.success_text));
|
||||
break;
|
||||
case DISCONNECTING:
|
||||
showProfile(true);
|
||||
showDisconnectDialog(name);
|
||||
mStateView.setText(R.string.state_disconnecting);
|
||||
mStateView.setTextColor(stateBaseColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean reportError(String name, State state, ErrorState error)
|
||||
{
|
||||
if (mError != ErrorState.NO_ERROR)
|
||||
{ /* we are currently reporting an error which was not yet dismissed */
|
||||
error = mError;
|
||||
name = mErrorProfileName;
|
||||
}
|
||||
else if (error != ErrorState.NO_ERROR && (state == State.CONNECTING || state == State.CONNECTED))
|
||||
{ /* while initiating we report errors */
|
||||
mError = error;
|
||||
mErrorProfileName = name;
|
||||
}
|
||||
else
|
||||
{ /* ignore all other errors */
|
||||
error = ErrorState.NO_ERROR;
|
||||
}
|
||||
if (error == ErrorState.NO_ERROR)
|
||||
{
|
||||
hideErrorDialog();
|
||||
return false;
|
||||
}
|
||||
else if (mErrorDialog != null)
|
||||
{ /* we already show the dialog */
|
||||
return true;
|
||||
}
|
||||
hideProgressDialog();
|
||||
mProfileNameView.setText(name);
|
||||
showProfile(true);
|
||||
enableActionButton(false);
|
||||
mStateView.setText(R.string.state_error);
|
||||
mStateView.setTextColor(getResources().getColor(R.color.error_text));
|
||||
switch (error)
|
||||
{
|
||||
case AUTH_FAILED:
|
||||
showErrorDialog(R.string.error_auth_failed);
|
||||
break;
|
||||
case PEER_AUTH_FAILED:
|
||||
showErrorDialog(R.string.error_peer_auth_failed);
|
||||
break;
|
||||
case LOOKUP_FAILED:
|
||||
showErrorDialog(R.string.error_lookup_failed);
|
||||
break;
|
||||
case UNREACHABLE:
|
||||
showErrorDialog(R.string.error_unreachable);
|
||||
break;
|
||||
default:
|
||||
showErrorDialog(R.string.error_generic);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showProfile(boolean show)
|
||||
{
|
||||
mProfileView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
mProfileNameView.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void enableActionButton(boolean enable)
|
||||
{
|
||||
mActionButton.setEnabled(enable);
|
||||
mActionButton.setVisibility(enable ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void hideProgressDialog()
|
||||
{
|
||||
if (mProgressDialog != null)
|
||||
{
|
||||
mProgressDialog.dismiss();
|
||||
mProgressDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void hideErrorDialog()
|
||||
{
|
||||
if (mErrorDialog != null)
|
||||
{
|
||||
mErrorDialog.dismiss();
|
||||
mErrorDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showConnectDialog(String profile, String gateway)
|
||||
{
|
||||
mProgressDialog = new ProgressDialog(getActivity());
|
||||
mProgressDialog.setTitle(String.format(getString(R.string.connecting_title), profile));
|
||||
mProgressDialog.setMessage(String.format(getString(R.string.connecting_message), gateway));
|
||||
mProgressDialog.setIndeterminate(true);
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.setButton(getString(android.R.string.cancel),
|
||||
new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
if (mService != null)
|
||||
{
|
||||
mService.disconnect();
|
||||
}
|
||||
}
|
||||
});
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
private void showDisconnectDialog(String profile)
|
||||
{
|
||||
mProgressDialog = new ProgressDialog(getActivity());
|
||||
mProgressDialog.setMessage(getString(R.string.state_disconnecting));
|
||||
mProgressDialog.setIndeterminate(true);
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
private void showErrorDialog(int textid)
|
||||
{
|
||||
mErrorDialog = new AlertDialog.Builder(getActivity())
|
||||
.setMessage(getString(R.string.error_introduction) + " " + getString(textid))
|
||||
.setCancelable(false)
|
||||
.setNeutralButton(R.string.show_log, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
dialog.dismiss();
|
||||
Intent logIntent = new Intent(getActivity(), LogActivity.class);
|
||||
startActivity(logIntent);
|
||||
}
|
||||
})
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id)
|
||||
{
|
||||
dialog.dismiss();
|
||||
}
|
||||
}).create();
|
||||
mErrorDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog)
|
||||
{ /* clear the error */
|
||||
mError = ErrorState.NO_ERROR;
|
||||
mErrorDialog = null;
|
||||
updateView();
|
||||
}
|
||||
});
|
||||
mErrorDialog.show();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui.adapter;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.http.SslCertificate;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TrustedCertificateAdapter extends BaseAdapter
|
||||
{
|
||||
private final ArrayList<CertEntry> mContent;
|
||||
private final Context mContext;
|
||||
|
||||
public class CertEntry implements Comparable<CertEntry>
|
||||
{
|
||||
public X509Certificate mCert;
|
||||
public String mAlias;
|
||||
public String mDisplayName;
|
||||
|
||||
public CertEntry(String alias, X509Certificate cert)
|
||||
{
|
||||
mCert = cert;
|
||||
mAlias = alias;
|
||||
}
|
||||
|
||||
public String getDisplayText()
|
||||
{
|
||||
if (mDisplayName == null)
|
||||
{
|
||||
SslCertificate cert = new SslCertificate(mCert);
|
||||
String o = cert.getIssuedTo().getOName();
|
||||
String ou = cert.getIssuedTo().getUName();
|
||||
String cn = cert.getIssuedTo().getCName();
|
||||
if (!o.isEmpty())
|
||||
{
|
||||
mDisplayName = o;
|
||||
if (!cn.isEmpty())
|
||||
{
|
||||
mDisplayName = mDisplayName + ", " + cn;
|
||||
}
|
||||
else if (!ou.isEmpty())
|
||||
{
|
||||
mDisplayName = mDisplayName + ", " + ou;
|
||||
}
|
||||
}
|
||||
else if (!cn.isEmpty())
|
||||
{
|
||||
mDisplayName = cn;
|
||||
}
|
||||
else
|
||||
{
|
||||
mDisplayName = cert.getIssuedTo().getDName();
|
||||
}
|
||||
}
|
||||
return mDisplayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CertEntry another)
|
||||
{
|
||||
return getDisplayText().compareToIgnoreCase(another.getDisplayText());
|
||||
}
|
||||
}
|
||||
|
||||
public TrustedCertificateAdapter(Context context,
|
||||
Hashtable<String, X509Certificate> content)
|
||||
{
|
||||
mContext = context;
|
||||
mContent = new ArrayList<TrustedCertificateAdapter.CertEntry>();
|
||||
for (Entry<String, X509Certificate> entry : content.entrySet())
|
||||
{
|
||||
mContent.add(new CertEntry(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
Collections.sort(mContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return mContent.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position)
|
||||
{
|
||||
return mContent.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position (index) of the entry with the given alias.
|
||||
*
|
||||
* @param alias alias of the item to find
|
||||
* @return the position (index) in the list
|
||||
*/
|
||||
public int getItemPosition(String alias)
|
||||
{
|
||||
for (int i = 0; i < mContent.size(); i++)
|
||||
{
|
||||
if (mContent.get(i).mAlias.equals(alias))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
final View certView = inflater.inflate(R.layout.trusted_certificates_item, null);
|
||||
final TextView certText = (TextView)certView.findViewById(R.id.certificate_name);
|
||||
certText.setText(mContent.get(position).getDisplayText());
|
||||
return certView;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 <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.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.ui.adapter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.strongswan.android.R;
|
||||
import org.strongswan.android.data.VpnProfile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class VpnProfileAdapter extends ArrayAdapter<VpnProfile>
|
||||
{
|
||||
private final int resource;
|
||||
private final List<VpnProfile> items;
|
||||
|
||||
public VpnProfileAdapter(Context context, int resource,
|
||||
List<VpnProfile> items)
|
||||
{
|
||||
super(context, resource, items);
|
||||
this.resource = resource;
|
||||
this.items = items;
|
||||
sortItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
View vpnProfileView;
|
||||
if (convertView != null)
|
||||
{
|
||||
vpnProfileView = convertView;
|
||||
}
|
||||
else
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
vpnProfileView = inflater.inflate(resource, null);
|
||||
}
|
||||
VpnProfile profile = getItem(position);
|
||||
TextView tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_name);
|
||||
tv.setText(profile.getName());
|
||||
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_gateway);
|
||||
tv.setText(getContext().getString(R.string.profile_gateway_label) + " " + profile.getGateway());
|
||||
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_username);
|
||||
tv.setText(getContext().getString(R.string.profile_username_label) + " " + profile.getUsername());
|
||||
return vpnProfileView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged()
|
||||
{
|
||||
sortItems();
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void sortItems()
|
||||
{
|
||||
Collections.sort(this.items, new Comparator<VpnProfile>() {
|
||||
@Override
|
||||
public int compare(VpnProfile lhs, VpnProfile rhs)
|
||||
{
|
||||
return lhs.getName().compareToIgnoreCase(rhs.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -44,7 +44,7 @@ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \
|
|||
encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \
|
||||
kernel/kernel_handler.c kernel/kernel_handler.h \
|
||||
network/receiver.c network/receiver.h network/sender.c network/sender.h \
|
||||
network/packet.c network/packet.h network/socket.c network/socket.h \
|
||||
network/socket.c network/socket.h \
|
||||
network/socket_manager.c network/socket_manager.h \
|
||||
processing/jobs/acquire_job.c processing/jobs/acquire_job.h \
|
||||
processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \
|
||||
|
|
|
@ -42,7 +42,7 @@ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \
|
|||
encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \
|
||||
kernel/kernel_handler.c kernel/kernel_handler.h \
|
||||
network/receiver.c network/receiver.h network/sender.c network/sender.h \
|
||||
network/packet.c network/packet.h network/socket.c network/socket.h \
|
||||
network/socket.c network/socket.h \
|
||||
network/socket_manager.c network/socket_manager.h \
|
||||
processing/jobs/acquire_job.c processing/jobs/acquire_job.h \
|
||||
processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \
|
||||
|
|
|
@ -102,7 +102,6 @@ static void destroy(private_daemon_t *this)
|
|||
/* cancel all threads and wait for their termination */
|
||||
lib->processor->cancel(lib->processor);
|
||||
|
||||
DESTROY_IF(this->public.receiver);
|
||||
#ifdef ME
|
||||
DESTROY_IF(this->public.connect_manager);
|
||||
DESTROY_IF(this->public.mediation_manager);
|
||||
|
@ -118,7 +117,6 @@ static void destroy(private_daemon_t *this)
|
|||
DESTROY_IF(this->public.eap);
|
||||
DESTROY_IF(this->public.xauth);
|
||||
DESTROY_IF(this->public.backends);
|
||||
DESTROY_IF(this->public.sender);
|
||||
DESTROY_IF(this->public.socket);
|
||||
DESTROY_IF(this->public.caps);
|
||||
|
||||
|
@ -142,17 +140,44 @@ METHOD(daemon_t, start, void,
|
|||
DEFAULT_THREADS, charon->name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize/deinitialize sender and receiver
|
||||
*/
|
||||
static bool sender_receiver_cb(void *plugin, plugin_feature_t *feature,
|
||||
bool reg, private_daemon_t *this)
|
||||
{
|
||||
if (reg)
|
||||
{
|
||||
this->public.receiver = receiver_create();
|
||||
if (!this->public.receiver)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
this->public.sender = sender_create();
|
||||
}
|
||||
else
|
||||
{
|
||||
DESTROY_IF(this->public.receiver);
|
||||
DESTROY_IF(this->public.sender);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(daemon_t, initialize, bool,
|
||||
private_daemon_t *this, char *plugins)
|
||||
{
|
||||
static plugin_feature_t features[] = {
|
||||
plugin_feature_t features[] = {
|
||||
PLUGIN_PROVIDE(CUSTOM, "libcharon"),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA1),
|
||||
PLUGIN_DEPENDS(RNG, RNG_STRONG),
|
||||
PLUGIN_DEPENDS(NONCE_GEN),
|
||||
PLUGIN_DEPENDS(CUSTOM, "libcharon-receiver"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "kernel-ipsec"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "kernel-net"),
|
||||
PLUGIN_DEPENDS(CUSTOM, "socket"),
|
||||
PLUGIN_CALLBACK((plugin_feature_callback_t)sender_receiver_cb, this),
|
||||
PLUGIN_PROVIDE(CUSTOM, "libcharon-receiver"),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA1),
|
||||
PLUGIN_DEPENDS(RNG, RNG_STRONG),
|
||||
PLUGIN_DEPENDS(CUSTOM, "socket"),
|
||||
};
|
||||
lib->plugins->add_static_features(lib->plugins, charon->name, features,
|
||||
countof(features), TRUE);
|
||||
|
@ -170,12 +195,6 @@ METHOD(daemon_t, initialize, bool,
|
|||
{
|
||||
return FALSE;
|
||||
}
|
||||
this->public.sender = sender_create();
|
||||
this->public.receiver = receiver_create();
|
||||
if (this->public.receiver == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Queue start_action job */
|
||||
lib->processor->queue_job(lib->processor, (job_t*)start_action_job_create());
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
typedef struct message_t message_t;
|
||||
|
||||
#include <library.h>
|
||||
#include <network/packet.h>
|
||||
#include <encoding/payloads/ike_header.h>
|
||||
#include <encoding/payloads/notify_payload.h>
|
||||
#include <sa/keymat.h>
|
||||
#include <sa/ike_sa_id.h>
|
||||
#include <utils/packet.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
|
||||
#include <daemon.h>
|
||||
#include <network/socket.h>
|
||||
#include <network/packet.h>
|
||||
#include <processing/jobs/job.h>
|
||||
#include <processing/jobs/process_message_job.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
#include <crypto/hashers/hasher.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
/** lifetime of a cookie, in seconds */
|
||||
#define COOKIE_LIFETIME 10
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
typedef struct receiver_t receiver_t;
|
||||
|
||||
#include <library.h>
|
||||
#include <network/packet.h>
|
||||
#include <utils/host.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
/**
|
||||
* Callback called for any received UDP encapsulated ESP packet.
|
||||
|
|
|
@ -87,7 +87,6 @@ METHOD(sender_t, send_no_marker, void,
|
|||
|
||||
src = packet->get_source(packet);
|
||||
dst = packet->get_destination(packet);
|
||||
DBG1(DBG_NET, "sending packet: from %#H to %#H", src, dst);
|
||||
|
||||
if (this->send_delay)
|
||||
{
|
||||
|
@ -124,6 +123,8 @@ METHOD(sender_t, send_, void,
|
|||
/* if neither source nor destination port is 500 we add a Non-ESP marker */
|
||||
src = packet->get_source(packet);
|
||||
dst = packet->get_destination(packet);
|
||||
DBG1(DBG_NET, "sending packet: from %#H to %#H", src, dst);
|
||||
|
||||
if (dst->get_port(dst) != IKEV2_UDP_PORT &&
|
||||
src->get_port(src) != IKEV2_UDP_PORT)
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
typedef struct sender_t sender_t;
|
||||
|
||||
#include <library.h>
|
||||
#include <network/packet.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
/**
|
||||
* Callback job responsible for sending IKE packets over the socket.
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
typedef struct socket_t socket_t;
|
||||
|
||||
#include <library.h>
|
||||
#include <network/packet.h>
|
||||
#include <utils/packet.h>
|
||||
#include <utils/enumerator.h>
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
|
|
|
@ -488,7 +488,7 @@ METHOD(ike_sa_t, send_keepalive, void,
|
|||
data.ptr[0] = 0xFF;
|
||||
data.len = 1;
|
||||
packet->set_data(packet, data);
|
||||
DBG1(DBG_IKE, "sending keep alive");
|
||||
DBG1(DBG_IKE, "sending keep alive to %#H", this->other_host);
|
||||
charon->sender->send_no_marker(charon->sender, packet);
|
||||
diff = 0;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ typedef struct ike_sa_t ike_sa_t;
|
|||
#include <config/peer_cfg.h>
|
||||
#include <config/ike_cfg.h>
|
||||
#include <credentials/auth_cfg.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
/**
|
||||
* Timeout in seconds after that a half open IKE_SA gets deleted.
|
||||
|
|
|
@ -26,7 +26,7 @@ typedef struct ike_mobike_t ike_mobike_t;
|
|||
#include <library.h>
|
||||
#include <sa/ike_sa.h>
|
||||
#include <sa/task.h>
|
||||
#include <network/packet.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
/**
|
||||
* Task of type ike_mobike, detects and handles MOBIKE extension.
|
||||
|
|
|
@ -17,28 +17,6 @@
|
|||
|
||||
#include <hydra.h>
|
||||
|
||||
ENUM(ipsec_mode_names, MODE_TRANSPORT, MODE_DROP,
|
||||
"TRANSPORT",
|
||||
"TUNNEL",
|
||||
"BEET",
|
||||
"PASS",
|
||||
"DROP"
|
||||
);
|
||||
|
||||
ENUM(policy_dir_names, POLICY_IN, POLICY_FWD,
|
||||
"in",
|
||||
"out",
|
||||
"fwd"
|
||||
);
|
||||
|
||||
ENUM(ipcomp_transform_names, IPCOMP_NONE, IPCOMP_LZJH,
|
||||
"IPCOMP_NONE",
|
||||
"IPCOMP_OUI",
|
||||
"IPCOMP_DEFLATE",
|
||||
"IPCOMP_LZS",
|
||||
"IPCOMP_LZJH"
|
||||
);
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
|
|
|
@ -24,158 +24,13 @@
|
|||
#ifndef KERNEL_IPSEC_H_
|
||||
#define KERNEL_IPSEC_H_
|
||||
|
||||
typedef enum ipsec_mode_t ipsec_mode_t;
|
||||
typedef enum policy_dir_t policy_dir_t;
|
||||
typedef enum policy_type_t policy_type_t;
|
||||
typedef enum policy_priority_t policy_priority_t;
|
||||
typedef enum ipcomp_transform_t ipcomp_transform_t;
|
||||
typedef struct kernel_ipsec_t kernel_ipsec_t;
|
||||
typedef struct ipsec_sa_cfg_t ipsec_sa_cfg_t;
|
||||
typedef struct lifetime_cfg_t lifetime_cfg_t;
|
||||
typedef struct mark_t mark_t;
|
||||
|
||||
#include <utils/host.h>
|
||||
#include <crypto/prf_plus.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
/**
|
||||
* Mode of an IPsec SA.
|
||||
*/
|
||||
enum ipsec_mode_t {
|
||||
/** not using any encapsulation */
|
||||
MODE_NONE = 0,
|
||||
/** transport mode, no inner address */
|
||||
MODE_TRANSPORT = 1,
|
||||
/** tunnel mode, inner and outer addresses */
|
||||
MODE_TUNNEL,
|
||||
/** BEET mode, tunnel mode but fixed, bound inner addresses */
|
||||
MODE_BEET,
|
||||
/** passthrough policy for traffic without an IPsec SA */
|
||||
MODE_PASS,
|
||||
/** drop policy discarding traffic */
|
||||
MODE_DROP
|
||||
};
|
||||
|
||||
/**
|
||||
* enum names for ipsec_mode_t.
|
||||
*/
|
||||
extern enum_name_t *ipsec_mode_names;
|
||||
|
||||
/**
|
||||
* Direction of a policy. These are equal to those
|
||||
* defined in xfrm.h, but we want to stay implementation
|
||||
* neutral here.
|
||||
*/
|
||||
enum policy_dir_t {
|
||||
/** Policy for inbound traffic */
|
||||
POLICY_IN = 0,
|
||||
/** Policy for outbound traffic */
|
||||
POLICY_OUT = 1,
|
||||
/** Policy for forwarded traffic */
|
||||
POLICY_FWD = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum names for policy_dir_t.
|
||||
*/
|
||||
extern enum_name_t *policy_dir_names;
|
||||
|
||||
/**
|
||||
* Type of a policy.
|
||||
*/
|
||||
enum policy_type_t {
|
||||
/** Normal IPsec policy */
|
||||
POLICY_IPSEC = 1,
|
||||
/** Passthrough policy (traffic is ignored by IPsec) */
|
||||
POLICY_PASS,
|
||||
/** Drop policy (traffic is discarded) */
|
||||
POLICY_DROP,
|
||||
};
|
||||
|
||||
/**
|
||||
* High-level priority of a policy.
|
||||
*/
|
||||
enum policy_priority_t {
|
||||
/** Default priority */
|
||||
POLICY_PRIORITY_DEFAULT,
|
||||
/** Priority for trap policies */
|
||||
POLICY_PRIORITY_ROUTED,
|
||||
/** Priority for fallback drop policies */
|
||||
POLICY_PRIORITY_FALLBACK,
|
||||
};
|
||||
|
||||
/**
|
||||
* IPComp transform IDs, as in RFC 4306
|
||||
*/
|
||||
enum ipcomp_transform_t {
|
||||
IPCOMP_NONE = 0,
|
||||
IPCOMP_OUI = 1,
|
||||
IPCOMP_DEFLATE = 2,
|
||||
IPCOMP_LZS = 3,
|
||||
IPCOMP_LZJH = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum strings for ipcomp_transform_t.
|
||||
*/
|
||||
extern enum_name_t *ipcomp_transform_names;
|
||||
|
||||
/**
|
||||
* This struct contains details about IPsec SA(s) tied to a policy.
|
||||
*/
|
||||
struct ipsec_sa_cfg_t {
|
||||
/** mode of SA (tunnel, transport) */
|
||||
ipsec_mode_t mode;
|
||||
/** unique ID */
|
||||
u_int32_t reqid;
|
||||
/** details about ESP/AH */
|
||||
struct {
|
||||
/** TRUE if this protocol is used */
|
||||
bool use;
|
||||
/** SPI for ESP/AH */
|
||||
u_int32_t spi;
|
||||
} esp, ah;
|
||||
/** details about IPComp */
|
||||
struct {
|
||||
/** the IPComp transform used */
|
||||
u_int16_t transform;
|
||||
/** CPI for IPComp */
|
||||
u_int16_t cpi;
|
||||
} ipcomp;
|
||||
};
|
||||
|
||||
/**
|
||||
* A lifetime_cfg_t defines the lifetime limits of an SA.
|
||||
*
|
||||
* Set any of these values to 0 to ignore.
|
||||
*/
|
||||
struct lifetime_cfg_t {
|
||||
struct {
|
||||
/** Limit before the SA gets invalid. */
|
||||
u_int64_t life;
|
||||
/** Limit before the SA gets rekeyed. */
|
||||
u_int64_t rekey;
|
||||
/** The range of a random value subtracted from rekey. */
|
||||
u_int64_t jitter;
|
||||
} time, bytes, packets;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mark_t defines an optional mark in an IPsec SA.
|
||||
*/
|
||||
struct mark_t {
|
||||
/** Mark value */
|
||||
u_int32_t value;
|
||||
/** Mark mask */
|
||||
u_int32_t mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* Special mark value that uses the reqid of the CHILD_SA as mark
|
||||
*/
|
||||
#define MARK_REQID (0xFFFFFFFF)
|
||||
|
||||
/**
|
||||
* Interface to the ipsec subsystem of the kernel.
|
||||
*
|
||||
|
|
|
@ -3,14 +3,23 @@ include $(CLEAR_VARS)
|
|||
|
||||
# copy-n-paste from Makefile.am
|
||||
LOCAL_SRC_FILES := \
|
||||
ipsec.c ipsec.h
|
||||
ipsec.c ipsec.h \
|
||||
esp_context.c esp_context.h \
|
||||
esp_packet.c esp_packet.h \
|
||||
ip_packet.c ip_packet.h \
|
||||
ipsec_event_listener.h \
|
||||
ipsec_event_relay.c ipsec_event_relay.h \
|
||||
ipsec_policy.c ipsec_policy.h \
|
||||
ipsec_policy_mgr.c ipsec_policy_mgr.h \
|
||||
ipsec_processor.c ipsec_processor.h \
|
||||
ipsec_sa.c ipsec_sa.h \
|
||||
ipsec_sa_mgr.c ipsec_sa_mgr.h
|
||||
|
||||
# build libipsec ---------------------------------------------------------------
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(libvstr_PATH) \
|
||||
$(strongswan_PATH)/src/include \
|
||||
$(strongswan_PATH)/src/libhydra \
|
||||
$(strongswan_PATH)/src/libstrongswan
|
||||
|
||||
LOCAL_CFLAGS := $(strongswan_CFLAGS)
|
||||
|
@ -23,7 +32,7 @@ LOCAL_ARM_MODE := arm
|
|||
|
||||
LOCAL_PRELINK_MODULE := false
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += libstrongswan libhydra
|
||||
LOCAL_SHARED_LIBRARIES += libstrongswan
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
ipseclib_LTLIBRARIES = libipsec.la
|
||||
|
||||
libipsec_la_SOURCES = \
|
||||
ipsec.c ipsec.h
|
||||
ipsec.c ipsec.h \
|
||||
esp_context.c esp_context.h \
|
||||
esp_packet.c esp_packet.h \
|
||||
ip_packet.c ip_packet.h \
|
||||
ipsec_event_listener.h \
|
||||
ipsec_event_relay.c ipsec_event_relay.h \
|
||||
ipsec_policy.c ipsec_policy.h \
|
||||
ipsec_policy_mgr.c ipsec_policy_mgr.h \
|
||||
ipsec_processor.c ipsec_processor.h \
|
||||
ipsec_sa.c ipsec_sa.h \
|
||||
ipsec_sa_mgr.c ipsec_sa_mgr.h
|
||||
|
||||
libipsec_la_LIBADD =
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/src/libstrongswan
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/src/libstrongswan
|
||||
|
||||
EXTRA_DIST = Android.mk
|
||||
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* 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 <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 <limits.h>
|
||||
|
||||
#include "esp_context.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
#include <crypto/crypters/crypter.h>
|
||||
#include <crypto/signers/signer.h>
|
||||
|
||||
/**
|
||||
* Should be a multiple of 8
|
||||
*/
|
||||
#define ESP_DEFAULT_WINDOW_SIZE 128
|
||||
|
||||
typedef struct private_esp_context_t private_esp_context_t;
|
||||
|
||||
/**
|
||||
* Private additions to esp_context_t.
|
||||
*/
|
||||
struct private_esp_context_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
esp_context_t public;
|
||||
|
||||
/**
|
||||
* Crypter used to encrypt/decrypt ESP packets
|
||||
*/
|
||||
crypter_t *crypter;
|
||||
|
||||
/**
|
||||
* Signer to authenticate ESP packets
|
||||
*/
|
||||
signer_t *signer;
|
||||
|
||||
/**
|
||||
* The highest sequence number that was successfully verified
|
||||
* and authenticated, or assigned in an outbound context
|
||||
*/
|
||||
u_int32_t last_seqno;
|
||||
|
||||
/**
|
||||
* The bit in the window of the highest authenticated sequence number
|
||||
*/
|
||||
u_int seqno_index;
|
||||
|
||||
/**
|
||||
* The size of the anti-replay window (in bits)
|
||||
*/
|
||||
u_int window_size;
|
||||
|
||||
/**
|
||||
* The anti-replay window buffer
|
||||
*/
|
||||
chunk_t window;
|
||||
|
||||
/**
|
||||
* TRUE in case of an inbound ESP context
|
||||
*/
|
||||
bool inbound;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or unset a bit in the window.
|
||||
*/
|
||||
static inline void set_window_bit(private_esp_context_t *this,
|
||||
u_int index, bool set)
|
||||
{
|
||||
u_int i = index / CHAR_BIT;
|
||||
|
||||
if (set)
|
||||
{
|
||||
this->window.ptr[i] |= 1 << (index % CHAR_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->window.ptr[i] &= ~(1 << (index % CHAR_BIT));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a bit from the window.
|
||||
*/
|
||||
static inline bool get_window_bit(private_esp_context_t *this, u_int index)
|
||||
{
|
||||
u_int i = index / CHAR_BIT;
|
||||
|
||||
return this->window.ptr[i] & (1 << index % CHAR_BIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the supplied seqno is not already marked in the window
|
||||
*/
|
||||
static bool check_window(private_esp_context_t *this, u_int32_t seqno)
|
||||
{
|
||||
u_int offset;
|
||||
|
||||
offset = this->last_seqno - seqno;
|
||||
offset = (this->seqno_index - offset) % this->window_size;
|
||||
return !get_window_bit(this, offset);
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, verify_seqno, bool,
|
||||
private_esp_context_t *this, u_int32_t seqno)
|
||||
{
|
||||
if (!this->inbound)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (seqno > this->last_seqno)
|
||||
{ /* |----------------------------------------|
|
||||
* <---------^ ^ or <---------^ ^
|
||||
* WIN H S WIN H S
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
else if (seqno > 0 && this->window_size > this->last_seqno - seqno)
|
||||
{ /* |----------------------------------------|
|
||||
* <---------^ or <---------^
|
||||
* WIN ^ H WIN ^ H
|
||||
* S S
|
||||
*/
|
||||
return check_window(this, seqno);
|
||||
}
|
||||
else
|
||||
{ /* |----------------------------------------|
|
||||
* ^ <---------^
|
||||
* S WIN H
|
||||
*/
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, set_authenticated_seqno, void,
|
||||
private_esp_context_t *this, u_int32_t seqno)
|
||||
{
|
||||
u_int i, shift;
|
||||
|
||||
if (!this->inbound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (seqno > this->last_seqno)
|
||||
{ /* shift the window to the new highest authenticated seqno */
|
||||
shift = seqno - this->last_seqno;
|
||||
shift = shift < this->window_size ? shift : this->window_size;
|
||||
for (i = 0; i < shift; ++i)
|
||||
{
|
||||
this->seqno_index = (this->seqno_index + 1) % this->window_size;
|
||||
set_window_bit(this, this->seqno_index, FALSE);
|
||||
}
|
||||
set_window_bit(this, this->seqno_index, TRUE);
|
||||
this->last_seqno = seqno;
|
||||
}
|
||||
else
|
||||
{ /* seqno is inside the window, set the corresponding window bit */
|
||||
i = this->last_seqno - seqno;
|
||||
set_window_bit(this, (this->seqno_index - i) % this->window_size, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, get_seqno, u_int32_t,
|
||||
private_esp_context_t *this)
|
||||
{
|
||||
return this->last_seqno;
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, next_seqno, bool,
|
||||
private_esp_context_t *this, u_int32_t *seqno)
|
||||
{
|
||||
if (this->inbound || this->last_seqno == UINT32_MAX)
|
||||
{ /* inbound or segno would cycle */
|
||||
return FALSE;
|
||||
}
|
||||
*seqno = ++this->last_seqno;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, get_signer, signer_t *,
|
||||
private_esp_context_t *this)
|
||||
{
|
||||
return this->signer;
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, get_crypter, crypter_t *,
|
||||
private_esp_context_t *this)
|
||||
{
|
||||
return this->crypter;
|
||||
}
|
||||
|
||||
METHOD(esp_context_t, destroy, void,
|
||||
private_esp_context_t *this)
|
||||
{
|
||||
chunk_free(&this->window);
|
||||
DESTROY_IF(this->crypter);
|
||||
DESTROY_IF(this->signer);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key,
|
||||
int int_alg, chunk_t int_key, bool inbound)
|
||||
{
|
||||
private_esp_context_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_crypter = _get_crypter,
|
||||
.get_signer = _get_signer,
|
||||
.get_seqno = _get_seqno,
|
||||
.next_seqno = _next_seqno,
|
||||
.verify_seqno = _verify_seqno,
|
||||
.set_authenticated_seqno = _set_authenticated_seqno,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.inbound = inbound,
|
||||
.window_size = ESP_DEFAULT_WINDOW_SIZE,
|
||||
);
|
||||
|
||||
switch(enc_alg)
|
||||
{
|
||||
case ENCR_AES_CBC:
|
||||
this->crypter = lib->crypto->create_crypter(lib->crypto, enc_alg,
|
||||
enc_key.len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!this->crypter)
|
||||
{
|
||||
DBG1(DBG_ESP, "failed to create ESP context: unsupported encryption "
|
||||
"algorithm");
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!this->crypter->set_key(this->crypter, enc_key))
|
||||
{
|
||||
DBG1(DBG_ESP, "failed to create ESP context: setting encryption key "
|
||||
"failed");
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(int_alg)
|
||||
{
|
||||
case AUTH_HMAC_SHA1_96:
|
||||
case AUTH_HMAC_SHA2_256_128:
|
||||
case AUTH_HMAC_SHA2_384_192:
|
||||
case AUTH_HMAC_SHA2_512_256:
|
||||
this->signer = lib->crypto->create_signer(lib->crypto, int_alg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!this->signer)
|
||||
{
|
||||
DBG1(DBG_ESP, "failed to create ESP context: unsupported integrity "
|
||||
"algorithm");
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!this->signer->set_key(this->signer, int_key))
|
||||
{
|
||||
DBG1(DBG_ESP, "failed to create ESP context: setting signature key "
|
||||
"failed");
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (inbound)
|
||||
{
|
||||
this->window = chunk_alloc(this->window_size / CHAR_BIT + 1);
|
||||
memset(this->window.ptr, 0, this->window.len);
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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 <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 esp_context esp_context
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef ESP_CONTEXT_H_
|
||||
#define ESP_CONTEXT_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <crypto/crypters/crypter.h>
|
||||
#include <crypto/signers/signer.h>
|
||||
|
||||
typedef struct esp_context_t esp_context_t;
|
||||
|
||||
/**
|
||||
* ESP context, handles sequence numbers and maintains cryptographic primitives
|
||||
*/
|
||||
struct esp_context_t {
|
||||
|
||||
/**
|
||||
* Get the crypter.
|
||||
*
|
||||
* @return crypter
|
||||
*/
|
||||
crypter_t *(*get_crypter)(esp_context_t *this);
|
||||
|
||||
/**
|
||||
* Get the signer.
|
||||
*
|
||||
* @return signer
|
||||
*/
|
||||
signer_t *(*get_signer)(esp_context_t *this);
|
||||
|
||||
/**
|
||||
* Get the current outbound ESP sequence number or the highest authenticated
|
||||
* inbound sequence number.
|
||||
*
|
||||
* @return current sequence number, in host byte order
|
||||
*/
|
||||
u_int32_t (*get_seqno)(esp_context_t *this);
|
||||
|
||||
/**
|
||||
* Allocate the next outbound ESP sequence number.
|
||||
*
|
||||
* @param seqno the sequence number, in host byte order
|
||||
* @return FALSE if the sequence number cycled or inbound context
|
||||
*/
|
||||
bool (*next_seqno)(esp_context_t *this, u_int32_t *seqno);
|
||||
|
||||
/**
|
||||
* Verify an ESP sequence number. Checks whether a packet with this
|
||||
* sequence number was already received, using the anti-replay window.
|
||||
* This operation does not modify the internal state. After the sequence
|
||||
* number is successfully verified and the ESP packet is authenticated,
|
||||
* set_authenticated_seqno() should be called.
|
||||
*
|
||||
* @param seqno the sequence number to verify, in host byte order
|
||||
* @return TRUE when sequence number is valid
|
||||
*/
|
||||
bool (*verify_seqno)(esp_context_t *this, u_int32_t seqno);
|
||||
|
||||
/**
|
||||
* Adds a sequence number that was successfully verified and
|
||||
* authenticated. A user MUST call verify_seqno() immediately before
|
||||
* calling this method.
|
||||
*
|
||||
* @param seqno verified and authenticated seq number in host byte order
|
||||
*/
|
||||
void (*set_authenticated_seqno)(esp_context_t *this,
|
||||
u_int32_t seqno);
|
||||
|
||||
/**
|
||||
* Destroy an esp_context_t
|
||||
*/
|
||||
void (*destroy)(esp_context_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an esp_context_t instance
|
||||
*
|
||||
* @param enc_alg encryption algorithm
|
||||
* @param enc_key encryption key
|
||||
* @param int_alg integrity protection algorithm
|
||||
* @param int_key integrity protection key
|
||||
* @param inbound TRUE to create an inbound ESP context
|
||||
* @return ESP context instance, or NULL if creation fails
|
||||
*/
|
||||
esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key, int int_alg,
|
||||
chunk_t int_key, bool inbound);
|
||||
|
||||
#endif /** ESP_CONTEXT_H_ @}*/
|
||||
|
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* 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 <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 "esp_packet.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
#include <crypto/crypters/crypter.h>
|
||||
#include <crypto/signers/signer.h>
|
||||
#include <bio/bio_reader.h>
|
||||
#include <bio/bio_writer.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
typedef struct private_esp_packet_t private_esp_packet_t;
|
||||
|
||||
/**
|
||||
* Private additions to esp_packet_t.
|
||||
*/
|
||||
struct private_esp_packet_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
esp_packet_t public;
|
||||
|
||||
/**
|
||||
* Raw ESP packet
|
||||
*/
|
||||
packet_t *packet;
|
||||
|
||||
/**
|
||||
* Payload of this packet
|
||||
*/
|
||||
ip_packet_t *payload;
|
||||
|
||||
/**
|
||||
* Next Header info (e.g. IPPROTO_IPIP)
|
||||
*/
|
||||
u_int8_t next_header;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Forward declaration for clone()
|
||||
*/
|
||||
static private_esp_packet_t *esp_packet_create_internal(packet_t *packet);
|
||||
|
||||
METHOD(packet_t, set_source, void,
|
||||
private_esp_packet_t *this, host_t *src)
|
||||
{
|
||||
return this->packet->set_source(this->packet, src);
|
||||
}
|
||||
|
||||
METHOD2(esp_packet_t, packet_t, get_source, host_t*,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
return this->packet->get_source(this->packet);
|
||||
}
|
||||
|
||||
METHOD(packet_t, set_destination, void,
|
||||
private_esp_packet_t *this, host_t *dst)
|
||||
{
|
||||
return this->packet->set_destination(this->packet, dst);
|
||||
}
|
||||
|
||||
METHOD2(esp_packet_t, packet_t, get_destination, host_t*,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
return this->packet->get_destination(this->packet);
|
||||
}
|
||||
|
||||
METHOD(packet_t, get_data, chunk_t,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
return this->packet->get_data(this->packet);
|
||||
}
|
||||
|
||||
METHOD(packet_t, set_data, void,
|
||||
private_esp_packet_t *this, chunk_t data)
|
||||
{
|
||||
return this->packet->set_data(this->packet, data);
|
||||
}
|
||||
|
||||
METHOD(packet_t, skip_bytes, void,
|
||||
private_esp_packet_t *this, size_t bytes)
|
||||
{
|
||||
return this->packet->skip_bytes(this->packet, bytes);
|
||||
}
|
||||
|
||||
METHOD(packet_t, clone, packet_t*,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
private_esp_packet_t *pkt;
|
||||
|
||||
pkt = esp_packet_create_internal(this->packet->clone(this->packet));
|
||||
pkt->payload = this->payload ? this->payload->clone(this->payload) : NULL;
|
||||
pkt->next_header = this->next_header;
|
||||
return &pkt->public.packet;
|
||||
}
|
||||
|
||||
METHOD(esp_packet_t, parse_header, bool,
|
||||
private_esp_packet_t *this, u_int32_t *spi)
|
||||
{
|
||||
bio_reader_t *reader;
|
||||
u_int32_t seq;
|
||||
|
||||
reader = bio_reader_create(this->packet->get_data(this->packet));
|
||||
if (!reader->read_uint32(reader, spi) ||
|
||||
!reader->read_uint32(reader, &seq))
|
||||
{
|
||||
DBG1(DBG_ESP, "failed to parse ESP header: invalid length");
|
||||
reader->destroy(reader);
|
||||
return FALSE;
|
||||
}
|
||||
reader->destroy(reader);
|
||||
|
||||
DBG2(DBG_ESP, "parsed ESP header with SPI %.8x [seq %u]", *spi, seq);
|
||||
*spi = htonl(*spi);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check padding as specified in RFC 4303
|
||||
*/
|
||||
static bool check_padding(chunk_t padding)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < padding.len; ++i)
|
||||
{
|
||||
if (padding.ptr[i] != (u_int8_t)(i + 1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the padding from the payload and set the next header info
|
||||
*/
|
||||
static bool remove_padding(private_esp_packet_t *this, chunk_t plaintext)
|
||||
{
|
||||
u_int8_t next_header, pad_length;
|
||||
chunk_t padding, payload;
|
||||
bio_reader_t *reader;
|
||||
|
||||
reader = bio_reader_create(plaintext);
|
||||
if (!reader->read_uint8_end(reader, &next_header) ||
|
||||
!reader->read_uint8_end(reader, &pad_length))
|
||||
{
|
||||
DBG1(DBG_ESP, "parsing ESP payload failed: invalid length");
|
||||
goto failed;
|
||||
}
|
||||
if (!reader->read_data_end(reader, pad_length, &padding) ||
|
||||
!check_padding(padding))
|
||||
{
|
||||
DBG1(DBG_ESP, "parsing ESP payload failed: invalid padding");
|
||||
goto failed;
|
||||
}
|
||||
this->payload = ip_packet_create(reader->peek(reader));
|
||||
reader->destroy(reader);
|
||||
if (!this->payload)
|
||||
{
|
||||
DBG1(DBG_ESP, "parsing ESP payload failed: unsupported payload");
|
||||
return FALSE;
|
||||
}
|
||||
this->next_header = next_header;
|
||||
payload = this->payload->get_encoding(this->payload);
|
||||
|
||||
DBG3(DBG_ESP, "ESP payload:\n payload %B\n padding %B\n "
|
||||
"padding length = %hhu, next header = %hhu", &payload, &padding,
|
||||
pad_length, this->next_header);
|
||||
return TRUE;
|
||||
|
||||
failed:
|
||||
reader->destroy(reader);
|
||||
chunk_free(&plaintext);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(esp_packet_t, decrypt, status_t,
|
||||
private_esp_packet_t *this, esp_context_t *esp_context)
|
||||
{
|
||||
bio_reader_t *reader;
|
||||
u_int32_t spi, seq;
|
||||
chunk_t data, iv, icv, ciphertext, plaintext;
|
||||
crypter_t *crypter;
|
||||
signer_t *signer;
|
||||
|
||||
DESTROY_IF(this->payload);
|
||||
this->payload = NULL;
|
||||
|
||||
data = this->packet->get_data(this->packet);
|
||||
crypter = esp_context->get_crypter(esp_context);
|
||||
signer = esp_context->get_signer(esp_context);
|
||||
|
||||
reader = bio_reader_create(data);
|
||||
if (!reader->read_uint32(reader, &spi) ||
|
||||
!reader->read_uint32(reader, &seq) ||
|
||||
!reader->read_data(reader, crypter->get_iv_size(crypter), &iv) ||
|
||||
!reader->read_data_end(reader, signer->get_block_size(signer), &icv) ||
|
||||
reader->remaining(reader) % crypter->get_block_size(crypter))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP decryption failed: invalid length");
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
ciphertext = reader->peek(reader);
|
||||
reader->destroy(reader);
|
||||
|
||||
if (!esp_context->verify_seqno(esp_context, seq))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP sequence number verification failed:\n "
|
||||
"src %H, dst %H, SPI %.8x [seq %u]",
|
||||
get_source(this), get_destination(this), spi, seq);
|
||||
return VERIFY_ERROR;
|
||||
}
|
||||
DBG3(DBG_ESP, "ESP decryption:\n SPI %.8x [seq %u]\n IV %B\n "
|
||||
"encrypted %B\n ICV %B", spi, seq, &iv, &ciphertext, &icv);
|
||||
|
||||
if (!signer->get_signature(signer, chunk_create(data.ptr, 8), NULL) ||
|
||||
!signer->get_signature(signer, iv, NULL) ||
|
||||
!signer->verify_signature(signer, ciphertext, icv))
|
||||
{
|
||||
DBG1(DBG_ESP, "ICV verification failed!");
|
||||
return FAILED;
|
||||
}
|
||||
esp_context->set_authenticated_seqno(esp_context, seq);
|
||||
|
||||
if (!crypter->decrypt(crypter, ciphertext, iv, &plaintext))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP decryption failed");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!remove_padding(this, plaintext))
|
||||
{
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the padding as specified in RFC4303
|
||||
*/
|
||||
static void generate_padding(chunk_t padding)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < padding.len; ++i)
|
||||
{
|
||||
padding.ptr[i] = (u_int8_t)(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
METHOD(esp_packet_t, encrypt, status_t,
|
||||
private_esp_packet_t *this, esp_context_t *esp_context, u_int32_t spi)
|
||||
{
|
||||
chunk_t iv, icv, padding, payload, ciphertext, auth_data;
|
||||
bio_writer_t *writer;
|
||||
u_int32_t next_seqno;
|
||||
size_t blocksize, plainlen;
|
||||
crypter_t *crypter;
|
||||
signer_t *signer;
|
||||
rng_t *rng;
|
||||
|
||||
this->packet->set_data(this->packet, chunk_empty);
|
||||
|
||||
if (!esp_context->next_seqno(esp_context, &next_seqno))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP encapsulation failed: sequence numbers cycled");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
if (!rng)
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP encryption failed: could not find RNG");
|
||||
return NOT_FOUND;
|
||||
}
|
||||
crypter = esp_context->get_crypter(esp_context);
|
||||
signer = esp_context->get_signer(esp_context);
|
||||
|
||||
blocksize = crypter->get_block_size(crypter);
|
||||
iv.len = crypter->get_iv_size(crypter);
|
||||
icv.len = signer->get_block_size(signer);
|
||||
|
||||
/* plaintext = payload, padding, pad_length, next_header */
|
||||
payload = this->payload ? this->payload->get_encoding(this->payload)
|
||||
: chunk_empty;
|
||||
plainlen = payload.len + 2;
|
||||
padding.len = blocksize - (plainlen % blocksize);
|
||||
plainlen += padding.len;
|
||||
|
||||
/* len = spi, seq, IV, plaintext, ICV */
|
||||
writer = bio_writer_create(2 * sizeof(u_int32_t) + iv.len + plainlen +
|
||||
icv.len);
|
||||
writer->write_uint32(writer, ntohl(spi));
|
||||
writer->write_uint32(writer, next_seqno);
|
||||
|
||||
iv = writer->skip(writer, iv.len);
|
||||
if (!rng->get_bytes(rng, iv.len, iv.ptr))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP encryption failed: could not generate IV");
|
||||
writer->destroy(writer);
|
||||
rng->destroy(rng);
|
||||
return FAILED;
|
||||
}
|
||||
rng->destroy(rng);
|
||||
|
||||
/* plain-/ciphertext will start here */
|
||||
ciphertext = writer->get_buf(writer);
|
||||
ciphertext.ptr += ciphertext.len;
|
||||
ciphertext.len = plainlen;
|
||||
|
||||
writer->write_data(writer, payload);
|
||||
|
||||
padding = writer->skip(writer, padding.len);
|
||||
generate_padding(padding);
|
||||
|
||||
writer->write_uint8(writer, padding.len);
|
||||
writer->write_uint8(writer, this->next_header);
|
||||
|
||||
DBG3(DBG_ESP, "ESP before encryption:\n payload = %B\n padding = %B\n "
|
||||
"padding length = %hhu, next header = %hhu", &payload, &padding,
|
||||
(u_int8_t)padding.len, this->next_header);
|
||||
|
||||
/* encrypt the content inline */
|
||||
if (!crypter->encrypt(crypter, ciphertext, iv, NULL))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP encryption failed");
|
||||
writer->destroy(writer);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* calculate signature */
|
||||
auth_data = writer->get_buf(writer);
|
||||
icv = writer->skip(writer, icv.len);
|
||||
if (!signer->get_signature(signer, auth_data, icv.ptr))
|
||||
{
|
||||
DBG1(DBG_ESP, "ESP encryption failed: signature generation failed");
|
||||
writer->destroy(writer);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
DBG3(DBG_ESP, "ESP packet:\n SPI %.8x [seq %u]\n IV %B\n "
|
||||
"encrypted %B\n ICV %B", ntohl(spi), next_seqno, &iv,
|
||||
&ciphertext, &icv);
|
||||
|
||||
this->packet->set_data(this->packet, writer->extract_buf(writer));
|
||||
writer->destroy(writer);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(esp_packet_t, get_next_header, u_int8_t,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
return this->next_header;
|
||||
}
|
||||
|
||||
METHOD(esp_packet_t, get_payload, ip_packet_t*,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
return this->payload;
|
||||
}
|
||||
|
||||
METHOD(esp_packet_t, extract_payload, ip_packet_t*,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
ip_packet_t *payload;
|
||||
|
||||
payload = this->payload;
|
||||
this->payload = NULL;
|
||||
return payload;
|
||||
}
|
||||
|
||||
METHOD2(esp_packet_t, packet_t, destroy, void,
|
||||
private_esp_packet_t *this)
|
||||
{
|
||||
DESTROY_IF(this->payload);
|
||||
this->packet->destroy(this->packet);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static private_esp_packet_t *esp_packet_create_internal(packet_t *packet)
|
||||
{
|
||||
private_esp_packet_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.packet = {
|
||||
.set_source = _set_source,
|
||||
.get_source = _get_source,
|
||||
.set_destination = _set_destination,
|
||||
.get_destination = _get_destination,
|
||||
.get_data = _get_data,
|
||||
.set_data = _set_data,
|
||||
.skip_bytes = _skip_bytes,
|
||||
.clone = _clone,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.get_source = _get_source,
|
||||
.get_destination = _get_destination,
|
||||
.get_next_header = _get_next_header,
|
||||
.parse_header = _parse_header,
|
||||
.decrypt = _decrypt,
|
||||
.encrypt = _encrypt,
|
||||
.get_payload = _get_payload,
|
||||
.extract_payload = _extract_payload,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.packet = packet,
|
||||
.next_header = IPPROTO_NONE,
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
esp_packet_t *esp_packet_create_from_packet(packet_t *packet)
|
||||
{
|
||||
private_esp_packet_t *this;
|
||||
|
||||
this = esp_packet_create_internal(packet);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst,
|
||||
ip_packet_t *payload)
|
||||
{
|
||||
private_esp_packet_t *this;
|
||||
packet_t *packet;
|
||||
|
||||
packet = packet_create_from_data(src, dst, chunk_empty);
|
||||
this = esp_packet_create_internal(packet);
|
||||
this->payload = payload;
|
||||
if (payload)
|
||||
{
|
||||
this->next_header = payload->get_version(payload) == 4 ? IPPROTO_IPIP
|
||||
: IPPROTO_IPV6;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->next_header = IPPROTO_NONE;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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 <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 esp_packet esp_packet
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef ESP_PACKET_H_
|
||||
#define ESP_PACKET_H_
|
||||
|
||||
#include "ip_packet.h"
|
||||
#include "esp_context.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/host.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
typedef struct esp_packet_t esp_packet_t;
|
||||
|
||||
/**
|
||||
* ESP packet
|
||||
*/
|
||||
struct esp_packet_t {
|
||||
|
||||
/**
|
||||
* Implements packet_t interface to access the raw ESP packet
|
||||
*/
|
||||
packet_t packet;
|
||||
|
||||
/**
|
||||
* Get the source address of this packet
|
||||
*
|
||||
* @return source host
|
||||
*/
|
||||
host_t *(*get_source)(esp_packet_t *this);
|
||||
|
||||
/**
|
||||
* Get the destination address of this packet
|
||||
*
|
||||
* @return destination host
|
||||
*/
|
||||
host_t *(*get_destination)(esp_packet_t *this);
|
||||
|
||||
/**
|
||||
* Parse the packet header before decryption. Tries to read the SPI
|
||||
* from the packet to find a corresponding SA.
|
||||
*
|
||||
* @param spi parsed SPI, in network byte order
|
||||
* @return TRUE when successful, FALSE otherwise (e.g. when the
|
||||
* length of the packet is invalid)
|
||||
*/
|
||||
bool (*parse_header)(esp_packet_t *this, u_int32_t *spi);
|
||||
|
||||
/**
|
||||
* Authenticate and decrypt the packet. Also verifies the sequence number
|
||||
* using the supplied ESP context and updates the anti-replay window.
|
||||
*
|
||||
* @param esp_context ESP context of corresponding inbound IPsec SA
|
||||
* @return - SUCCESS if successfully authenticated,
|
||||
* decrypted and parsed
|
||||
* - PARSE_ERROR if the length of the packet or the
|
||||
* padding is invalid
|
||||
* - VERIFY_ERROR if the sequence number
|
||||
* verification failed
|
||||
* - FAILED if the ICV (MAC) check or the actual
|
||||
* decryption failed
|
||||
*/
|
||||
status_t (*decrypt)(esp_packet_t *this, esp_context_t *esp_context);
|
||||
|
||||
/**
|
||||
* Encapsulate and encrypt the packet. The sequence number will be generated
|
||||
* using the supplied ESP context.
|
||||
*
|
||||
* @param esp_context ESP context of corresponding outbound IPsec SA
|
||||
* @param spi SPI value to use, in network byte order
|
||||
* @return - SUCCESS if encrypted
|
||||
* - FAILED if sequence number cycled or any of the
|
||||
* cryptographic functions failed
|
||||
* - NOT_FOUND if no suitable RNG could be found
|
||||
*/
|
||||
status_t (*encrypt)(esp_packet_t *this, esp_context_t *esp_context,
|
||||
u_int32_t spi);
|
||||
|
||||
/**
|
||||
* Get the next header field of a packet.
|
||||
*
|
||||
* @note Packet has to be in the decrypted state.
|
||||
*
|
||||
* @return next header field
|
||||
*/
|
||||
u_int8_t (*get_next_header)(esp_packet_t *this);
|
||||
|
||||
/**
|
||||
* Get the plaintext payload of this packet.
|
||||
*
|
||||
* @return plaintext payload (internal data),
|
||||
* NULL if not decrypted
|
||||
*/
|
||||
ip_packet_t *(*get_payload)(esp_packet_t *this);
|
||||
|
||||
/**
|
||||
* Extract the plaintext payload from this packet.
|
||||
*
|
||||
* @return plaintext payload (has to be destroyed),
|
||||
* NULL if not decrypted
|
||||
*/
|
||||
ip_packet_t *(*extract_payload)(esp_packet_t *this);
|
||||
|
||||
/**
|
||||
* Destroy an esp_packet_t
|
||||
*/
|
||||
void (*destroy)(esp_packet_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ESP packet out of data from the wire.
|
||||
*
|
||||
* @param packet the packet data as received, gets owned
|
||||
* @return esp_packet_t instance
|
||||
*/
|
||||
esp_packet_t *esp_packet_create_from_packet(packet_t *packet);
|
||||
|
||||
/**
|
||||
* Create an ESP packet from a plaintext payload
|
||||
*
|
||||
* @param src source address
|
||||
* @param dst destination address
|
||||
* @param payload plaintext payload, gets owned
|
||||
* @return esp_packet_t instance
|
||||
*/
|
||||
esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst,
|
||||
ip_packet_t *payload);
|
||||
|
||||
#endif /** ESP_PACKET_H_ @}*/
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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 <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 "ip_packet.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#ifdef HAVE_NETINET_IP6_H
|
||||
#include <netinet/ip6.h>
|
||||
#endif
|
||||
|
||||
typedef struct private_ip_packet_t private_ip_packet_t;
|
||||
|
||||
/**
|
||||
* Private additions to ip_packet_t.
|
||||
*/
|
||||
struct private_ip_packet_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
ip_packet_t public;
|
||||
|
||||
/**
|
||||
* Source address
|
||||
*/
|
||||
host_t *src;
|
||||
|
||||
/**
|
||||
* Destination address
|
||||
*/
|
||||
host_t *dst;
|
||||
|
||||
/**
|
||||
* IP packet
|
||||
*/
|
||||
chunk_t packet;
|
||||
|
||||
/**
|
||||
* IP version
|
||||
*/
|
||||
u_int8_t version;
|
||||
|
||||
/**
|
||||
* Protocol|Next Header field
|
||||
*/
|
||||
u_int8_t next_header;
|
||||
|
||||
};
|
||||
|
||||
METHOD(ip_packet_t, get_version, u_int8_t,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
return this->version;
|
||||
}
|
||||
|
||||
METHOD(ip_packet_t, get_source, host_t*,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
return this->src;
|
||||
}
|
||||
|
||||
METHOD(ip_packet_t, get_destination, host_t*,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
return this->dst;
|
||||
}
|
||||
|
||||
METHOD(ip_packet_t, get_encoding, chunk_t,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
return this->packet;
|
||||
}
|
||||
|
||||
METHOD(ip_packet_t, get_next_header, u_int8_t,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
return this->next_header;
|
||||
}
|
||||
|
||||
METHOD(ip_packet_t, clone, ip_packet_t*,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
return ip_packet_create(this->packet);
|
||||
}
|
||||
|
||||
METHOD(ip_packet_t, destroy, void,
|
||||
private_ip_packet_t *this)
|
||||
{
|
||||
this->src->destroy(this->src);
|
||||
this->dst->destroy(this->dst);
|
||||
chunk_free(&this->packet);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ip_packet_t *ip_packet_create(chunk_t packet)
|
||||
{
|
||||
private_ip_packet_t *this;
|
||||
u_int8_t version, next_header;
|
||||
host_t *src, *dst;
|
||||
|
||||
if (packet.len < 1)
|
||||
{
|
||||
DBG1(DBG_ESP, "IP packet too short");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
version = (packet.ptr[0] & 0xf0) >> 4;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 4:
|
||||
{
|
||||
struct iphdr *ip;
|
||||
|
||||
if (packet.len < sizeof(struct iphdr))
|
||||
{
|
||||
DBG1(DBG_ESP, "IPv4 packet too short");
|
||||
goto failed;
|
||||
}
|
||||
ip = (struct iphdr*)packet.ptr;
|
||||
src = host_create_from_chunk(AF_INET,
|
||||
chunk_from_thing(ip->saddr), 0);
|
||||
dst = host_create_from_chunk(AF_INET,
|
||||
chunk_from_thing(ip->daddr), 0);
|
||||
next_header = ip->protocol;
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_NETINET_IP6_H
|
||||
case 6:
|
||||
{
|
||||
struct ip6_hdr *ip;
|
||||
|
||||
if (packet.len < sizeof(struct ip6_hdr))
|
||||
{
|
||||
DBG1(DBG_ESP, "IPv6 packet too short");
|
||||
goto failed;
|
||||
}
|
||||
ip = (struct ip6_hdr*)packet.ptr;
|
||||
src = host_create_from_chunk(AF_INET6,
|
||||
chunk_from_thing(ip->ip6_src), 0);
|
||||
dst = host_create_from_chunk(AF_INET6,
|
||||
chunk_from_thing(ip->ip6_dst), 0);
|
||||
next_header = ip->ip6_nxt;
|
||||
}
|
||||
#endif /* HAVE_NETINET_IP6_H */
|
||||
default:
|
||||
DBG1(DBG_ESP, "unsupported IP version");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_version = _get_version,
|
||||
.get_source = _get_source,
|
||||
.get_destination = _get_destination,
|
||||
.get_next_header = _get_next_header,
|
||||
.get_encoding = _get_encoding,
|
||||
.clone = _clone,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.src = src,
|
||||
.dst = dst,
|
||||
.packet = packet,
|
||||
.version = version,
|
||||
.next_header = next_header,
|
||||
);
|
||||
return &this->public;
|
||||
|
||||
failed:
|
||||
chunk_free(&packet);
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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 <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 ip_packet ip_packet
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IP_PACKET_H_
|
||||
#define IP_PACKET_H_
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/host.h>
|
||||
#include <utils/packet.h>
|
||||
|
||||
typedef struct ip_packet_t ip_packet_t;
|
||||
|
||||
/**
|
||||
* IP packet
|
||||
*/
|
||||
struct ip_packet_t {
|
||||
|
||||
/**
|
||||
* IP version of this packet
|
||||
*
|
||||
* @return ip version
|
||||
*/
|
||||
u_int8_t (*get_version)(ip_packet_t *this);
|
||||
|
||||
/**
|
||||
* Get the source address of this packet
|
||||
*
|
||||
* @return source host
|
||||
*/
|
||||
host_t *(*get_source)(ip_packet_t *this);
|
||||
|
||||
/**
|
||||
* Get the destination address of this packet
|
||||
*
|
||||
* @return destination host
|
||||
*/
|
||||
host_t *(*get_destination)(ip_packet_t *this);
|
||||
|
||||
/**
|
||||
* Get the protocol (IPv4) or next header (IPv6) field of this packet.
|
||||
*
|
||||
* @return protocol|next header field
|
||||
*/
|
||||
u_int8_t (*get_next_header)(ip_packet_t *this);
|
||||
|
||||
/**
|
||||
* Get the complete IP packet (including the header)
|
||||
*
|
||||
* @return IP packet (internal data)
|
||||
*/
|
||||
chunk_t (*get_encoding)(ip_packet_t *this);
|
||||
|
||||
/**
|
||||
* Clone the IP packet
|
||||
*
|
||||
* @return clone of the packet
|
||||
*/
|
||||
ip_packet_t *(*clone)(ip_packet_t *this);
|
||||
|
||||
/**
|
||||
* Destroy an ip_packet_t
|
||||
*/
|
||||
void (*destroy)(ip_packet_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an IP packet out of data from the wire (or decapsulated from another
|
||||
* packet).
|
||||
*
|
||||
* @note The raw IP packet gets either owned by the new object, or destroyed,
|
||||
* if the data is invalid.
|
||||
*
|
||||
* @param packet the IP packet (including header), gets owned
|
||||
* @return ip_packet_t instance, or NULL if invalid
|
||||
*/
|
||||
ip_packet_t *ip_packet_create(chunk_t packet);
|
||||
|
||||
#endif /** IP_PACKET_H_ @}*/
|
|
@ -1,4 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
|
@ -41,6 +43,10 @@ ipsec_t *ipsec;
|
|||
void libipsec_deinit()
|
||||
{
|
||||
private_ipsec_t *this = (private_ipsec_t*)ipsec;
|
||||
DESTROY_IF(this->public.processor);
|
||||
DESTROY_IF(this->public.events);
|
||||
DESTROY_IF(this->public.policies);
|
||||
DESTROY_IF(this->public.sas);
|
||||
free(this);
|
||||
ipsec = NULL;
|
||||
}
|
||||
|
@ -52,10 +58,7 @@ bool libipsec_init()
|
|||
{
|
||||
private_ipsec_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
},
|
||||
);
|
||||
INIT(this);
|
||||
ipsec = &this->public;
|
||||
|
||||
if (lib->integrity &&
|
||||
|
@ -64,6 +67,11 @@ bool libipsec_init()
|
|||
DBG1(DBG_LIB, "integrity check of libipsec failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
this->public.sas = ipsec_sa_mgr_create();
|
||||
this->public.policies = ipsec_policy_mgr_create();
|
||||
this->public.events = ipsec_event_relay_create();
|
||||
this->public.processor = ipsec_processor_create();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Giuliano Grassi
|
||||
* Copyright (C) 2012 Ralf Sager
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
|
@ -23,15 +25,40 @@
|
|||
#ifndef IPSEC_H_
|
||||
#define IPSEC_H_
|
||||
|
||||
typedef struct ipsec_t ipsec_t;
|
||||
#include "ipsec_sa_mgr.h"
|
||||
#include "ipsec_policy_mgr.h"
|
||||
#include "ipsec_event_relay.h"
|
||||
#include "ipsec_processor.h"
|
||||
|
||||
#include <library.h>
|
||||
|
||||
typedef struct ipsec_t ipsec_t;
|
||||
|
||||
/**
|
||||
* User space IPsec implementation.
|
||||
*/
|
||||
struct ipsec_t {
|
||||
|
||||
/**
|
||||
* IPsec SA manager instance
|
||||
*/
|
||||
ipsec_sa_mgr_t *sas;
|
||||
|
||||
/**
|
||||
* IPsec policy manager instance
|
||||
*/
|
||||
ipsec_policy_mgr_t *policies;
|
||||
|
||||
/**
|
||||
* Event relay instance
|
||||
*/
|
||||
ipsec_event_relay_t *events;
|
||||
|
||||
/**
|
||||
* IPsec processor instance
|
||||
*/
|
||||
ipsec_processor_t *processor;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 <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 ipsec_event_listener ipsec_event_listener
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_EVENT_LISTENER_H_
|
||||
#define IPSEC_EVENT_LISTENER_H_
|
||||
|
||||
typedef struct ipsec_event_listener_t ipsec_event_listener_t;
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* Listener interface for IPsec events
|
||||
*
|
||||
* All methods are optional.
|
||||
*/
|
||||
struct ipsec_event_listener_t {
|
||||
|
||||
/**
|
||||
* Called when the lifetime of an IPsec SA expired
|
||||
*
|
||||
* @param reqid reqid of the expired SA
|
||||
* @param protocol protocol of the expired SA
|
||||
* @param spi spi of the expired SA
|
||||
* @param hard TRUE if this is a hard expire, FALSE otherwise
|
||||
*/
|
||||
void (*expire)(u_int32_t reqid, u_int8_t protocol, u_int32_t spi,
|
||||
bool hard);
|
||||
|
||||
};
|
||||
|
||||
#endif /** IPSEC_EVENT_LISTENER_H_ @}*/
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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 <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 "ipsec_event_relay.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <utils/linked_list.h>
|
||||
#include <utils/blocking_queue.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
typedef struct private_ipsec_event_relay_t private_ipsec_event_relay_t;
|
||||
|
||||
/**
|
||||
* Private additions to ipsec_event_relay_t.
|
||||
*/
|
||||
struct private_ipsec_event_relay_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
ipsec_event_relay_t public;
|
||||
|
||||
/**
|
||||
* Registered listeners
|
||||
*/
|
||||
linked_list_t *listeners;
|
||||
|
||||
/**
|
||||
* Lock to safely access the list of listeners
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
|
||||
/**
|
||||
* Blocking queue for events
|
||||
*/
|
||||
blocking_queue_t *queue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper struct used to manage events in a queue
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
/**
|
||||
* Type of the event
|
||||
*/
|
||||
enum {
|
||||
IPSEC_EVENT_EXPIRE,
|
||||
} type;
|
||||
|
||||
/**
|
||||
* Reqid of the SA, if any
|
||||
*/
|
||||
u_int32_t reqid;
|
||||
|
||||
/**
|
||||
* SPI of the SA, if any
|
||||
*/
|
||||
u_int32_t spi;
|
||||
|
||||
/**
|
||||
* Additional data for specific event types
|
||||
*/
|
||||
union {
|
||||
|
||||
struct {
|
||||
/** Protocol of the SA */
|
||||
u_int8_t protocol;
|
||||
/** TRUE in case of a hard expire */
|
||||
bool hard;
|
||||
} expire;
|
||||
|
||||
} data;
|
||||
|
||||
} ipsec_event_t;
|
||||
|
||||
/**
|
||||
* Dequeue events and relay them to listeners
|
||||
*/
|
||||
static job_requeue_t handle_events(private_ipsec_event_relay_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
ipsec_event_listener_t *current;
|
||||
ipsec_event_t *event;
|
||||
|
||||
event = this->queue->dequeue(this->queue);
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->listeners->create_enumerator(this->listeners);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case IPSEC_EVENT_EXPIRE:
|
||||
if (current->expire)
|
||||
{
|
||||
current->expire(event->reqid, event->data.expire.protocol,
|
||||
event->spi, event->data.expire.hard);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
METHOD(ipsec_event_relay_t, expire, void,
|
||||
private_ipsec_event_relay_t *this, u_int32_t reqid, u_int8_t protocol,
|
||||
u_int32_t spi, bool hard)
|
||||
{
|
||||
ipsec_event_t *event;
|
||||
|
||||
INIT(event,
|
||||
.type = IPSEC_EVENT_EXPIRE,
|
||||
.reqid = reqid,
|
||||
.spi = spi,
|
||||
.data = {
|
||||
.expire = {
|
||||
.protocol = protocol,
|
||||
.hard = hard,
|
||||
},
|
||||
},
|
||||
);
|
||||
this->queue->enqueue(this->queue, event);
|
||||
}
|
||||
|
||||
METHOD(ipsec_event_relay_t, register_listener, void,
|
||||
private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->listeners->insert_last(this->listeners, listener);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(ipsec_event_relay_t, unregister_listener, void,
|
||||
private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->listeners->remove(this->listeners, listener, NULL);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(ipsec_event_relay_t, destroy, void,
|
||||
private_ipsec_event_relay_t *this)
|
||||
{
|
||||
this->queue->destroy_function(this->queue, free);
|
||||
this->listeners->destroy(this->listeners);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ipsec_event_relay_t *ipsec_event_relay_create()
|
||||
{
|
||||
private_ipsec_event_relay_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.expire = _expire,
|
||||
.register_listener = _register_listener,
|
||||
.unregister_listener = _unregister_listener,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.listeners = linked_list_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
.queue = blocking_queue_create(),
|
||||
);
|
||||
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create((callback_job_cb_t)handle_events, this,
|
||||
NULL, (callback_job_cancel_t)return_false));
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 <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 ipsec_event_relay ipsec_event_relay
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_EVENT_RELAY_H_
|
||||
#define IPSEC_EVENT_RELAY_H_
|
||||
|
||||
#include "ipsec_event_listener.h"
|
||||
|
||||
#include <library.h>
|
||||
|
||||
typedef struct ipsec_event_relay_t ipsec_event_relay_t;
|
||||
|
||||
/**
|
||||
* Event relay manager.
|
||||
*
|
||||
* Used to notify upper layers about changes
|
||||
*/
|
||||
struct ipsec_event_relay_t {
|
||||
|
||||
/**
|
||||
* Raise an expire event.
|
||||
*
|
||||
* @param reqid reqid of the expired IPsec SA
|
||||
* @param protocol protocol (e.g ESP) of the expired SA
|
||||
* @param spi SPI of the expired SA
|
||||
* @param hard TRUE for a hard expire, FALSE otherwise
|
||||
*/
|
||||
void (*expire)(ipsec_event_relay_t *this, u_int32_t reqid,
|
||||
u_int8_t protocol, u_int32_t spi, bool hard);
|
||||
|
||||
/**
|
||||
* Register a listener to events raised by this manager
|
||||
*
|
||||
* @param listener the listener to register
|
||||
*/
|
||||
void (*register_listener)(ipsec_event_relay_t *this,
|
||||
ipsec_event_listener_t *listener);
|
||||
|
||||
/**
|
||||
* Unregister a listener
|
||||
*
|
||||
* @param listener the listener to unregister
|
||||
*/
|
||||
void (*unregister_listener)(ipsec_event_relay_t *this,
|
||||
ipsec_event_listener_t *listener);
|
||||
|
||||
/**
|
||||
* Destroy an ipsec_event_relay_t
|
||||
*/
|
||||
void (*destroy)(ipsec_event_relay_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ipsec_event_relay_t instance
|
||||
*
|
||||
* @return IPsec event relay instance
|
||||
*/
|
||||
ipsec_event_relay_t *ipsec_event_relay_create();
|
||||
|
||||
#endif /** IPSEC_EVENT_RELAY_H_ @}*/
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* 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 <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 "ipsec_policy.h"
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
typedef struct private_ipsec_policy_t private_ipsec_policy_t;
|
||||
|
||||
/**
|
||||
* Private additions to ipsec_policy_t.
|
||||
*/
|
||||
struct private_ipsec_policy_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
ipsec_policy_t public;
|
||||
|
||||
/**
|
||||
* SA source address
|
||||
*/
|
||||
host_t *src;
|
||||
|
||||
/**
|
||||
* SA destination address
|
||||
*/
|
||||
host_t *dst;
|
||||
|
||||
/**
|
||||
* Source traffic selector
|
||||
*/
|
||||
traffic_selector_t *src_ts;
|
||||
|
||||
/**
|
||||
* Destination traffic selector
|
||||
*/
|
||||
traffic_selector_t *dst_ts;
|
||||
|
||||
/**
|
||||
* If any of the two TS has a protocol selector we cache it here
|
||||
*/
|
||||
u_int8_t protocol;
|
||||
|
||||
/**
|
||||
* Traffic direction
|
||||
*/
|
||||
policy_dir_t direction;
|
||||
|
||||
/**
|
||||
* Policy type
|
||||
*/
|
||||
policy_type_t type;
|
||||
|
||||
/**
|
||||
* SA configuration
|
||||
*/
|
||||
ipsec_sa_cfg_t sa;
|
||||
|
||||
/**
|
||||
* Mark
|
||||
*/
|
||||
mark_t mark;
|
||||
|
||||
/**
|
||||
* Policy priority
|
||||
*/
|
||||
policy_priority_t priority;
|
||||
|
||||
/**
|
||||
* Reference counter
|
||||
*/
|
||||
refcount_t refcount;
|
||||
|
||||
};
|
||||
|
||||
METHOD(ipsec_policy_t, match, bool,
|
||||
private_ipsec_policy_t *this, traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
|
||||
mark_t mark, policy_priority_t priority)
|
||||
{
|
||||
return (this->direction == direction &&
|
||||
this->priority == priority &&
|
||||
this->sa.reqid == reqid &&
|
||||
memeq(&this->mark, &mark, sizeof(mark_t)) &&
|
||||
this->src_ts->equals(this->src_ts, src_ts) &&
|
||||
this->dst_ts->equals(this->dst_ts, dst_ts));
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, match_packet, bool,
|
||||
private_ipsec_policy_t *this, ip_packet_t *packet)
|
||||
{
|
||||
u_int8_t proto = packet->get_next_header(packet);
|
||||
host_t *src = packet->get_source(packet),
|
||||
*dst = packet->get_destination(packet);
|
||||
|
||||
return (!this->protocol || this->protocol == proto) &&
|
||||
this->src_ts->includes(this->src_ts, src) &&
|
||||
this->dst_ts->includes(this->dst_ts, dst);
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_source_ts, traffic_selector_t*,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
return this->src_ts;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_destination_ts, traffic_selector_t*,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
return this->dst_ts;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_reqid, u_int32_t,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
return this->sa.reqid;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_direction, policy_dir_t,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
return this->direction;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_priority, policy_priority_t,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
return this->priority;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_type, policy_type_t,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
return this->type;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, get_ref, ipsec_policy_t*,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
ref_get(&this->refcount);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_t, destroy, void,
|
||||
private_ipsec_policy_t *this)
|
||||
{
|
||||
if (ref_put(&this->refcount))
|
||||
{
|
||||
this->src->destroy(this->src);
|
||||
this->dst->destroy(this->dst);
|
||||
this->src_ts->destroy(this->src_ts);
|
||||
this->dst_ts->destroy(this->dst_ts);
|
||||
free(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ipsec_policy_t *ipsec_policy_create(host_t *src, host_t *dst,
|
||||
traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts,
|
||||
policy_dir_t direction, policy_type_t type,
|
||||
ipsec_sa_cfg_t *sa, mark_t mark,
|
||||
policy_priority_t priority)
|
||||
{
|
||||
private_ipsec_policy_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.match = _match,
|
||||
.match_packet = _match_packet,
|
||||
.get_source_ts = _get_source_ts,
|
||||
.get_destination_ts = _get_destination_ts,
|
||||
.get_direction = _get_direction,
|
||||
.get_priority = _get_priority,
|
||||
.get_reqid = _get_reqid,
|
||||
.get_type = _get_type,
|
||||
.get_ref = _get_ref,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.src = src->clone(src),
|
||||
.dst = dst->clone(dst),
|
||||
.src_ts = src_ts->clone(src_ts),
|
||||
.dst_ts = dst_ts->clone(dst_ts),
|
||||
.protocol = max(src_ts->get_protocol(src_ts),
|
||||
dst_ts->get_protocol(dst_ts)),
|
||||
.direction = direction,
|
||||
.type = type,
|
||||
.sa = *sa,
|
||||
.mark = mark,
|
||||
.priority = priority,
|
||||
.refcount = 1,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 <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 ipsec_policy ipsec_policy
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_POLICY_H
|
||||
#define IPSEC_POLICY_H
|
||||
|
||||
#include "ip_packet.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/host.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
|
||||
typedef struct ipsec_policy_t ipsec_policy_t;
|
||||
|
||||
/**
|
||||
* IPsec Policy
|
||||
*/
|
||||
struct ipsec_policy_t {
|
||||
|
||||
/**
|
||||
* Get the source traffic selector of this policy
|
||||
*
|
||||
* @return the source traffic selector
|
||||
*/
|
||||
traffic_selector_t *(*get_source_ts)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Get the destination traffic selector of this policy
|
||||
*
|
||||
* @return the destination traffic selector
|
||||
*/
|
||||
traffic_selector_t *(*get_destination_ts)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Get the direction of this policy
|
||||
*
|
||||
* @return direction
|
||||
*/
|
||||
policy_dir_t (*get_direction)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Get the priority of this policy
|
||||
*
|
||||
* @return priority
|
||||
*/
|
||||
policy_priority_t (*get_priority)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Get the type of this policy (e.g. IPsec)
|
||||
*
|
||||
* @return the policy type
|
||||
*/
|
||||
policy_type_t (*get_type)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Get the reqid associated to this policy
|
||||
*
|
||||
* @return the reqid
|
||||
*/
|
||||
u_int32_t (*get_reqid)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Get another reference to this policy
|
||||
*
|
||||
* @return additional reference to the policy
|
||||
*/
|
||||
ipsec_policy_t *(*get_ref)(ipsec_policy_t *this);
|
||||
|
||||
/**
|
||||
* Check if this policy matches all given parameters
|
||||
*
|
||||
* @param src_ts source traffic selector
|
||||
* @param dst_ts destination traffic selector
|
||||
* @param direction traffic direction
|
||||
* @param reqid reqid of the policy
|
||||
* @param mark mark for this policy
|
||||
* @param prioirty policy priority
|
||||
* @return TRUE if policy matches all parameters
|
||||
*/
|
||||
bool (*match)(ipsec_policy_t *this, traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction,
|
||||
u_int32_t reqid, mark_t mark, policy_priority_t priority);
|
||||
|
||||
/**
|
||||
* Check if this policy matches the given IP packet
|
||||
*
|
||||
* @param packet IP packet
|
||||
* @return TRUE if policy matches the packet
|
||||
*/
|
||||
bool (*match_packet)(ipsec_policy_t *this, ip_packet_t *packet);
|
||||
|
||||
/**
|
||||
* Destroy an ipsec_policy_t
|
||||
*/
|
||||
void (*destroy)(ipsec_policy_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ipsec_policy_t instance
|
||||
*
|
||||
* @param src source address of SA
|
||||
* @param dst dest address of SA
|
||||
* @param src_ts traffic selector to match traffic source
|
||||
* @param dst_ts traffic selector to match traffic dest
|
||||
* @param direction direction of traffic, POLICY_(IN|OUT|FWD)
|
||||
* @param type type of policy, POLICY_(IPSEC|PASS|DROP)
|
||||
* @param sa details about the SA(s) tied to this policy
|
||||
* @param mark mark for this policy
|
||||
* @param priority priority of this policy
|
||||
* @return ipsec policy instance
|
||||
*/
|
||||
ipsec_policy_t *ipsec_policy_create(host_t *src, host_t *dst,
|
||||
traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts,
|
||||
policy_dir_t direction, policy_type_t type,
|
||||
ipsec_sa_cfg_t *sa, mark_t mark,
|
||||
policy_priority_t priority);
|
||||
|
||||
#endif /** IPSEC_POLICY_H @}*/
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* 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 <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 "ipsec_policy_mgr.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
/** Base priority for installed policies */
|
||||
#define PRIO_BASE 512
|
||||
|
||||
typedef struct private_ipsec_policy_mgr_t private_ipsec_policy_mgr_t;
|
||||
|
||||
/**
|
||||
* Private additions to ipsec_policy_mgr_t.
|
||||
*/
|
||||
struct private_ipsec_policy_mgr_t {
|
||||
|
||||
/**
|
||||
* Public members of ipsec_policy_mgr_t.
|
||||
*/
|
||||
ipsec_policy_mgr_t public;
|
||||
|
||||
/**
|
||||
* Installed policies (ipsec_policy_entry_t*)
|
||||
*/
|
||||
linked_list_t *policies;
|
||||
|
||||
/**
|
||||
* Lock to safely access the list of policies
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper struct to store policies in a list sorted by the same pseudo-priority
|
||||
* used by the NETLINK kernel interface.
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
/**
|
||||
* Priority used to sort policies
|
||||
*/
|
||||
u_int32_t priority;
|
||||
|
||||
/**
|
||||
* The policy
|
||||
*/
|
||||
ipsec_policy_t *policy;
|
||||
|
||||
} ipsec_policy_entry_t;
|
||||
|
||||
/**
|
||||
* Calculate the pseudo-priority to sort policies. This is the same algorithm
|
||||
* used by the NETLINK kernel interface (i.e. high priority -> low value).
|
||||
*/
|
||||
static u_int32_t calculate_priority(policy_priority_t policy_priority,
|
||||
traffic_selector_t *src,
|
||||
traffic_selector_t *dst)
|
||||
{
|
||||
u_int32_t priority = PRIO_BASE;
|
||||
u_int16_t port;
|
||||
u_int8_t mask, proto;
|
||||
host_t *net;
|
||||
|
||||
switch (policy_priority)
|
||||
{
|
||||
case POLICY_PRIORITY_FALLBACK:
|
||||
priority <<= 1;
|
||||
/* fall-through */
|
||||
case POLICY_PRIORITY_ROUTED:
|
||||
priority <<= 1;
|
||||
/* fall-through */
|
||||
case POLICY_PRIORITY_DEFAULT:
|
||||
break;
|
||||
}
|
||||
/* calculate priority based on selector size, small size = high prio */
|
||||
src->to_subnet(src, &net, &mask);
|
||||
priority -= mask;
|
||||
proto = src->get_protocol(src);
|
||||
port = net->get_port(net);
|
||||
net->destroy(net);
|
||||
|
||||
dst->to_subnet(dst, &net, &mask);
|
||||
priority -= mask;
|
||||
proto = max(proto, dst->get_protocol(dst));
|
||||
port = max(port, net->get_port(net));
|
||||
net->destroy(net);
|
||||
|
||||
priority <<= 2; /* make some room for the two flags */
|
||||
priority += port ? 0 : 2;
|
||||
priority += proto ? 0 : 1;
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a policy entry
|
||||
*/
|
||||
static ipsec_policy_entry_t *policy_entry_create(ipsec_policy_t *policy)
|
||||
{
|
||||
ipsec_policy_entry_t *this;
|
||||
|
||||
INIT(this,
|
||||
.policy = policy,
|
||||
.priority = calculate_priority(policy->get_priority(policy),
|
||||
policy->get_source_ts(policy),
|
||||
policy->get_destination_ts(policy)),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a policy entry
|
||||
*/
|
||||
static void policy_entry_destroy(ipsec_policy_entry_t *this)
|
||||
{
|
||||
this->policy->destroy(this->policy);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_mgr_t, add_policy, status_t,
|
||||
private_ipsec_policy_mgr_t *this, host_t *src, host_t *dst,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
|
||||
policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
|
||||
policy_priority_t priority)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
ipsec_policy_entry_t *entry, *current;
|
||||
ipsec_policy_t *policy;
|
||||
|
||||
if (type != POLICY_IPSEC || direction == POLICY_FWD)
|
||||
{ /* we ignore these policies as we currently have no use for them */
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
DBG2(DBG_ESP, "adding policy %R === %R %N", src_ts, dst_ts,
|
||||
policy_dir_names, direction);
|
||||
|
||||
policy = ipsec_policy_create(src, dst, src_ts, dst_ts, direction, type, sa,
|
||||
mark, priority);
|
||||
entry = policy_entry_create(policy);
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
enumerator = this->policies->create_enumerator(this->policies);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
if (current->priority >= entry->priority)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->policies->insert_before(this->policies, enumerator, entry);
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_mgr_t, del_policy, status_t,
|
||||
private_ipsec_policy_mgr_t *this, traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
|
||||
mark_t mark, policy_priority_t policy_priority)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
ipsec_policy_entry_t *current, *found = NULL;
|
||||
u_int32_t priority;
|
||||
|
||||
if (direction == POLICY_FWD)
|
||||
{ /* we ignore these policies as we currently have no use for them */
|
||||
return SUCCESS;
|
||||
}
|
||||
DBG2(DBG_ESP, "deleting policy %R === %R %N", src_ts, dst_ts,
|
||||
policy_dir_names, direction);
|
||||
|
||||
priority = calculate_priority(policy_priority, src_ts, dst_ts);
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
enumerator = this->policies->create_enumerator(this->policies);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
if (current->priority == priority &&
|
||||
current->policy->match(current->policy, src_ts, dst_ts, direction,
|
||||
reqid, mark, policy_priority))
|
||||
{
|
||||
this->policies->remove_at(this->policies, enumerator);
|
||||
found = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
if (found)
|
||||
{
|
||||
policy_entry_destroy(found);
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_mgr_t, flush_policies, status_t,
|
||||
private_ipsec_policy_mgr_t *this)
|
||||
{
|
||||
ipsec_policy_entry_t *entry;
|
||||
|
||||
DBG2(DBG_ESP, "flushing policies");
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
while (this->policies->remove_last(this->policies,
|
||||
(void**)&entry) == SUCCESS)
|
||||
{
|
||||
policy_entry_destroy(entry);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_mgr_t, find_by_packet, ipsec_policy_t*,
|
||||
private_ipsec_policy_mgr_t *this, ip_packet_t *packet, bool inbound)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
ipsec_policy_entry_t *current;
|
||||
ipsec_policy_t *found = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->policies->create_enumerator(this->policies);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
ipsec_policy_t *policy = current->policy;
|
||||
|
||||
if ((inbound == (policy->get_direction(policy) == POLICY_IN)) &&
|
||||
policy->match_packet(policy, packet))
|
||||
{
|
||||
found = policy->get_ref(policy);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
METHOD(ipsec_policy_mgr_t, destroy, void,
|
||||
private_ipsec_policy_mgr_t *this)
|
||||
{
|
||||
flush_policies(this);
|
||||
this->policies->destroy(this->policies);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ipsec_policy_mgr_t *ipsec_policy_mgr_create()
|
||||
{
|
||||
private_ipsec_policy_mgr_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.add_policy = _add_policy,
|
||||
.del_policy = _del_policy,
|
||||
.flush_policies = _flush_policies,
|
||||
.find_by_packet = _find_by_packet,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.policies = linked_list_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* 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 <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 ipsec_policy_mgr ipsec_policy_mgr
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_POLICY_MGR_H_
|
||||
#define IPSEC_POLICY_MGR_H_
|
||||
|
||||
#include "ipsec_policy.h"
|
||||
#include "ip_packet.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/host.h>
|
||||
#include <utils/linked_list.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
|
||||
typedef struct ipsec_policy_mgr_t ipsec_policy_mgr_t;
|
||||
|
||||
/**
|
||||
* IPsec policy manager
|
||||
*
|
||||
* The first methods are modeled after those in kernel_ipsec_t.
|
||||
*
|
||||
* @note Only policies of type POLICY_IPSEC are currently used, also policies
|
||||
* with direction POLICY_FWD are ignored. Any packets that do not match an
|
||||
* installed policy will be dropped.
|
||||
*/
|
||||
struct ipsec_policy_mgr_t {
|
||||
|
||||
/**
|
||||
* Add a policy
|
||||
*
|
||||
* A policy is always associated to an SA. Traffic which matches a
|
||||
* policy is handled by the SA with the same reqid.
|
||||
*
|
||||
* @param src source address of SA
|
||||
* @param dst dest address of SA
|
||||
* @param src_ts traffic selector to match traffic source
|
||||
* @param dst_ts traffic selector to match traffic dest
|
||||
* @param direction direction of traffic, POLICY_(IN|OUT|FWD)
|
||||
* @param type type of policy, POLICY_(IPSEC|PASS|DROP)
|
||||
* @param sa details about the SA(s) tied to this policy
|
||||
* @param mark mark for this policy
|
||||
* @param priority priority of this policy
|
||||
* @return SUCCESS if operation completed
|
||||
*/
|
||||
status_t (*add_policy)(ipsec_policy_mgr_t *this,
|
||||
host_t *src, host_t *dst, traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts, policy_dir_t direction,
|
||||
policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
|
||||
policy_priority_t priority);
|
||||
|
||||
/**
|
||||
* Remove a policy
|
||||
*
|
||||
* @param src_ts traffic selector to match traffic source
|
||||
* @param dst_ts traffic selector to match traffic dest
|
||||
* @param direction direction of traffic, POLICY_(IN|OUT|FWD)
|
||||
* @param reqid unique ID of the associated SA
|
||||
* @param mark optional mark
|
||||
* @param priority priority of the policy
|
||||
* @return SUCCESS if operation completed
|
||||
*/
|
||||
status_t (*del_policy)(ipsec_policy_mgr_t *this,
|
||||
traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts,
|
||||
policy_dir_t direction, u_int32_t reqid, mark_t mark,
|
||||
policy_priority_t priority);
|
||||
|
||||
/**
|
||||
* Flush all policies
|
||||
*
|
||||
* @return SUCCESS if operation completed
|
||||
*/
|
||||
status_t (*flush_policies)(ipsec_policy_mgr_t *this);
|
||||
|
||||
/**
|
||||
* Find the policy that matches the given IP packet best
|
||||
*
|
||||
* @param packet IP packet to match
|
||||
* @param inbound TRUE for an inbound packet
|
||||
* @return reference to the policy, or NULL if none found
|
||||
*/
|
||||
ipsec_policy_t *(*find_by_packet)(ipsec_policy_mgr_t *this,
|
||||
ip_packet_t *packet, bool inbound);
|
||||
|
||||
/**
|
||||
* Destroy an ipsec_policy_mgr_t
|
||||
*/
|
||||
void (*destroy)(ipsec_policy_mgr_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ipsec_policy_mgr instance
|
||||
*
|
||||
* @return ipsec_policy_mgr
|
||||
*/
|
||||
ipsec_policy_mgr_t *ipsec_policy_mgr_create();
|
||||
|
||||
#endif /** IPSEC_POLICY_MGR_H_ @}*/
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* 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 <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 "ipsec.h"
|
||||
#include "ipsec_processor.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <library.h>
|
||||
#include <threading/rwlock.h>
|
||||
#include <utils/blocking_queue.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
typedef struct private_ipsec_processor_t private_ipsec_processor_t;
|
||||
|
||||
/**
|
||||
* Private additions to ipsec_processor_t.
|
||||
*/
|
||||
struct private_ipsec_processor_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
ipsec_processor_t public;
|
||||
|
||||
/**
|
||||
* Queue for inbound packets (esp_packet_t*)
|
||||
*/
|
||||
blocking_queue_t *inbound_queue;
|
||||
|
||||
/**
|
||||
* Queue for outbound packets (ip_packet_t*)
|
||||
*/
|
||||
blocking_queue_t *outbound_queue;
|
||||
|
||||
/**
|
||||
* Registered inbound callback
|
||||
*/
|
||||
struct {
|
||||
ipsec_inbound_cb_t cb;
|
||||
void *data;
|
||||
} inbound;
|
||||
|
||||
/**
|
||||
* Registered outbound callback
|
||||
*/
|
||||
struct {
|
||||
ipsec_outbound_cb_t cb;
|
||||
void *data;
|
||||
} outbound;
|
||||
|
||||
/**
|
||||
* Lock used to synchronize access to the callbacks
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deliver an inbound IP packet to the registered listener
|
||||
*/
|
||||
static void deliver_inbound(private_ipsec_processor_t *this,
|
||||
esp_packet_t *packet)
|
||||
{
|
||||
this->lock->read_lock(this->lock);
|
||||
if (this->inbound.cb)
|
||||
{
|
||||
this->inbound.cb(this->inbound.data, packet->extract_payload(packet));
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG2(DBG_ESP, "no inbound callback registered, dropping packet");
|
||||
}
|
||||
packet->destroy(packet);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes inbound packets
|
||||
*/
|
||||
static job_requeue_t process_inbound(private_ipsec_processor_t *this)
|
||||
{
|
||||
esp_packet_t *packet;
|
||||
ipsec_sa_t *sa;
|
||||
u_int8_t next_header;
|
||||
u_int32_t spi;
|
||||
|
||||
packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue);
|
||||
|
||||
if (!packet->parse_header(packet, &spi))
|
||||
{
|
||||
packet->destroy(packet);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi,
|
||||
packet->get_destination(packet));
|
||||
if (!sa)
|
||||
{
|
||||
DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA");
|
||||
packet->destroy(packet);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
if (!sa->is_inbound(sa))
|
||||
{
|
||||
DBG1(DBG_ESP, "error: IPsec SA is not inbound");
|
||||
packet->destroy(packet);
|
||||
ipsec->sas->checkin(ipsec->sas, sa);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS)
|
||||
{
|
||||
ipsec->sas->checkin(ipsec->sas, sa);
|
||||
packet->destroy(packet);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
ipsec->sas->checkin(ipsec->sas, sa);
|
||||
|
||||
next_header = packet->get_next_header(packet);
|
||||
switch (next_header)
|
||||
{
|
||||
case IPPROTO_IPIP:
|
||||
case IPPROTO_IPV6:
|
||||
{
|
||||
ipsec_policy_t *policy;
|
||||
ip_packet_t *ip_packet;
|
||||
|
||||
ip_packet = packet->get_payload(packet);
|
||||
policy = ipsec->policies->find_by_packet(ipsec->policies,
|
||||
ip_packet, TRUE);
|
||||
if (policy)
|
||||
{ /* TODO-IPSEC: update policy/sa stats? */
|
||||
deliver_inbound(this, packet);
|
||||
policy->destroy(policy);
|
||||
break;
|
||||
}
|
||||
DBG1(DBG_ESP, "discarding inbound IP packet due to policy");
|
||||
/* no matching policy found, fall-through */
|
||||
}
|
||||
case IPPROTO_NONE:
|
||||
/* discard dummy packets */
|
||||
/* fall-through */
|
||||
default:
|
||||
packet->destroy(packet);
|
||||
break;
|
||||
}
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ESP packet using the registered outbound callback
|
||||
*/
|
||||
static void send_outbound(private_ipsec_processor_t *this,
|
||||
esp_packet_t *packet)
|
||||
{
|
||||
this->lock->read_lock(this->lock);
|
||||
if (this->outbound.cb)
|
||||
{
|
||||
this->outbound.cb(this->outbound.data, packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG2(DBG_ESP, "no outbound callback registered, dropping packet");
|
||||
packet->destroy(packet);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes outbound packets
|
||||
*/
|
||||
static job_requeue_t process_outbound(private_ipsec_processor_t *this)
|
||||
{
|
||||
ipsec_policy_t *policy;
|
||||
esp_packet_t *esp_packet;
|
||||
ip_packet_t *packet;
|
||||
ipsec_sa_t *sa;
|
||||
host_t *src, *dst;
|
||||
|
||||
packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
|
||||
|
||||
policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE);
|
||||
if (!policy)
|
||||
{
|
||||
DBG1(DBG_ESP, "no matching outbound IPsec policy for %H == %H",
|
||||
packet->get_source(packet), packet->get_destination(packet));
|
||||
packet->destroy(packet);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
|
||||
FALSE);
|
||||
if (!sa)
|
||||
{ /* TODO-IPSEC: send an acquire to uppper layer */
|
||||
DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
|
||||
"dropping packet", policy->get_reqid(policy));
|
||||
packet->destroy(packet);
|
||||
policy->destroy(policy);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
src = sa->get_source(sa);
|
||||
dst = sa->get_destination(sa);
|
||||
esp_packet = esp_packet_create_from_payload(src->clone(src),
|
||||
dst->clone(dst), packet);
|
||||
if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa),
|
||||
sa->get_spi(sa)) != SUCCESS)
|
||||
{
|
||||
ipsec->sas->checkin(ipsec->sas, sa);
|
||||
esp_packet->destroy(esp_packet);
|
||||
policy->destroy(policy);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
/* TODO-IPSEC: update policy/sa counters? */
|
||||
ipsec->sas->checkin(ipsec->sas, sa);
|
||||
policy->destroy(policy);
|
||||
send_outbound(this, esp_packet);
|
||||
return JOB_REQUEUE_DIRECT;
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, queue_inbound, void,
|
||||
private_ipsec_processor_t *this, esp_packet_t *packet)
|
||||
{
|
||||
this->inbound_queue->enqueue(this->inbound_queue, packet);
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, queue_outbound, void,
|
||||
private_ipsec_processor_t *this, ip_packet_t *packet)
|
||||
{
|
||||
this->outbound_queue->enqueue(this->outbound_queue, packet);
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, register_inbound, void,
|
||||
private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->inbound.cb = cb;
|
||||
this->inbound.data = data;
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, unregister_inbound, void,
|
||||
private_ipsec_processor_t *this, ipsec_inbound_cb_t cb)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
if (this->inbound.cb == cb)
|
||||
{
|
||||
this->inbound.cb = NULL;
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, register_outbound, void,
|
||||
private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->outbound.cb = cb;
|
||||
this->outbound.data = data;
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, unregister_outbound, void,
|
||||
private_ipsec_processor_t *this, ipsec_outbound_cb_t cb)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
if (this->outbound.cb == cb)
|
||||
{
|
||||
this->outbound.cb = NULL;
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(ipsec_processor_t, destroy, void,
|
||||
private_ipsec_processor_t *this)
|
||||
{
|
||||
this->inbound_queue->destroy_offset(this->inbound_queue,
|
||||
offsetof(esp_packet_t, destroy));
|
||||
this->outbound_queue->destroy_offset(this->outbound_queue,
|
||||
offsetof(ip_packet_t, destroy));
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ipsec_processor_t *ipsec_processor_create()
|
||||
{
|
||||
private_ipsec_processor_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.queue_inbound = _queue_inbound,
|
||||
.queue_outbound = _queue_outbound,
|
||||
.register_inbound = _register_inbound,
|
||||
.unregister_inbound = _unregister_inbound,
|
||||
.register_outbound = _register_outbound,
|
||||
.unregister_outbound = _unregister_outbound,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.inbound_queue = blocking_queue_create(),
|
||||
.outbound_queue = blocking_queue_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create((callback_job_cb_t)process_inbound, this,
|
||||
NULL, (callback_job_cancel_t)return_false));
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create((callback_job_cb_t)process_outbound, this,
|
||||
NULL, (callback_job_cancel_t)return_false));
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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 <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 ipsec_processor ipsec_processor
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_PROCESSOR_H_
|
||||
#define IPSEC_PROCESSOR_H_
|
||||
|
||||
#include "ip_packet.h"
|
||||
#include "esp_packet.h"
|
||||
|
||||
typedef struct ipsec_processor_t ipsec_processor_t;
|
||||
|
||||
/**
|
||||
* Callback called to deliver an inbound plaintext packet.
|
||||
*
|
||||
* @param data data supplied during registration of the callback
|
||||
* @param packet plaintext IP packet to deliver
|
||||
*/
|
||||
typedef void (*ipsec_inbound_cb_t)(void *data, ip_packet_t *packet);
|
||||
|
||||
/**
|
||||
* Callback called to send an ESP packet.
|
||||
*
|
||||
* @note The ESP packet currently comes without IP header (and without UDP
|
||||
* header in case of UDP encapsulation)
|
||||
*
|
||||
* @param data data supplied during registration of the callback
|
||||
* @param packet ESP packet to send
|
||||
*/
|
||||
typedef void (*ipsec_outbound_cb_t)(void *data, esp_packet_t *packet);
|
||||
|
||||
/**
|
||||
* IPsec processor
|
||||
*/
|
||||
struct ipsec_processor_t {
|
||||
|
||||
/**
|
||||
* Queue an inbound ESP packet for processing.
|
||||
*
|
||||
* @param packet the ESP packet to process
|
||||
*/
|
||||
void (*queue_inbound)(ipsec_processor_t *this, esp_packet_t *packet);
|
||||
|
||||
/**
|
||||
* Queue an outbound plaintext IP packet for processing.
|
||||
*
|
||||
* @param packet the plaintext IP packet
|
||||
*/
|
||||
void (*queue_outbound)(ipsec_processor_t *this, ip_packet_t *packet);
|
||||
|
||||
/**
|
||||
* Register the callback used to deliver inbound plaintext packets.
|
||||
*
|
||||
* @param cb the inbound callback function
|
||||
* @param data optional data provided to callback
|
||||
*/
|
||||
void (*register_inbound)(ipsec_processor_t *this, ipsec_inbound_cb_t cb,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Unregister a previously registered inbound callback.
|
||||
*
|
||||
* @param cb previously registered callback function
|
||||
*/
|
||||
void (*unregister_inbound)(ipsec_processor_t *this,
|
||||
ipsec_inbound_cb_t cb);
|
||||
|
||||
/**
|
||||
* Register the callback used to send outbound ESP packets.
|
||||
*
|
||||
* @param cb the outbound callback function
|
||||
* @param data optional data provided to callback
|
||||
*/
|
||||
void (*register_outbound)(ipsec_processor_t *this, ipsec_outbound_cb_t cb,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Unregister a previously registered outbound callback.
|
||||
*
|
||||
* @param cb previously registered callback function
|
||||
*/
|
||||
void (*unregister_outbound)(ipsec_processor_t *this,
|
||||
ipsec_outbound_cb_t cb);
|
||||
|
||||
/**
|
||||
* Destroy an ipsec_processor_t.
|
||||
*/
|
||||
void (*destroy)(ipsec_processor_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ipsec_processor_t instance
|
||||
*
|
||||
* @return IPsec processor instance
|
||||
*/
|
||||
ipsec_processor_t *ipsec_processor_create();
|
||||
|
||||
#endif /** IPSEC_PROCESSOR_H_ @}*/
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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 <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 "ipsec_sa.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
|
||||
typedef struct private_ipsec_sa_t private_ipsec_sa_t;
|
||||
|
||||
/**
|
||||
* Private additions to ipsec_sa_t.
|
||||
*/
|
||||
struct private_ipsec_sa_t {
|
||||
|
||||
/**
|
||||
* Public members
|
||||
*/
|
||||
ipsec_sa_t public;
|
||||
|
||||
/**
|
||||
* SPI of this SA
|
||||
*/
|
||||
u_int32_t spi;
|
||||
|
||||
/**
|
||||
* Source address
|
||||
*/
|
||||
host_t *src;
|
||||
|
||||
/**
|
||||
* Destination address
|
||||
*/
|
||||
host_t *dst;
|
||||
|
||||
/**
|
||||
* Protocol
|
||||
*/
|
||||
u_int8_t protocol;
|
||||
|
||||
/**
|
||||
* Reqid of this SA
|
||||
*/
|
||||
u_int32_t reqid;
|
||||
|
||||
/**
|
||||
* Lifetime configuration
|
||||
*/
|
||||
lifetime_cfg_t lifetime;
|
||||
|
||||
/**
|
||||
* IPsec mode
|
||||
*/
|
||||
ipsec_mode_t mode;
|
||||
|
||||
/**
|
||||
* TRUE if extended sequence numbers are used
|
||||
*/
|
||||
bool esn;
|
||||
|
||||
/**
|
||||
* TRUE if this is an inbound SA
|
||||
*/
|
||||
bool inbound;
|
||||
|
||||
/**
|
||||
* ESP context
|
||||
*/
|
||||
esp_context_t *esp_context;
|
||||
};
|
||||
|
||||
METHOD(ipsec_sa_t, get_source, host_t*,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->src;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, get_destination, host_t*,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->dst;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, get_spi, u_int32_t,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->spi;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, get_reqid, u_int32_t,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->reqid;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, get_protocol, u_int8_t,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->protocol;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, get_lifetime, lifetime_cfg_t*,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return &this->lifetime;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, is_inbound, bool,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->inbound;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, get_esp_context, esp_context_t*,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
return this->esp_context;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, match_by_spi_dst, bool,
|
||||
private_ipsec_sa_t *this, u_int32_t spi, host_t *dst)
|
||||
{
|
||||
return this->spi == spi && this->dst->ip_equals(this->dst, dst);
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, match_by_spi_src_dst, bool,
|
||||
private_ipsec_sa_t *this, u_int32_t spi, host_t *src, host_t *dst)
|
||||
{
|
||||
return this->spi == spi && this->src->ip_equals(this->src, src) &&
|
||||
this->dst->ip_equals(this->dst, dst);
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, match_by_reqid, bool,
|
||||
private_ipsec_sa_t *this, u_int32_t reqid, bool inbound)
|
||||
{
|
||||
return this->reqid == reqid && this->inbound == inbound;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_t, destroy, void,
|
||||
private_ipsec_sa_t *this)
|
||||
{
|
||||
this->src->destroy(this->src);
|
||||
this->dst->destroy(this->dst);
|
||||
DESTROY_IF(this->esp_context);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
|
||||
u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc,
|
||||
lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
|
||||
u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode,
|
||||
u_int16_t ipcomp, u_int16_t cpi, bool encap, bool esn, bool inbound,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
|
||||
{
|
||||
private_ipsec_sa_t *this;
|
||||
|
||||
if (protocol != IPPROTO_ESP)
|
||||
{
|
||||
DBG1(DBG_ESP, " IPsec SA: protocol not supported");
|
||||
return NULL;
|
||||
}
|
||||
if (!encap)
|
||||
{
|
||||
DBG1(DBG_ESP, " IPsec SA: only UDP encapsulation is supported");
|
||||
return NULL;
|
||||
}
|
||||
if (esn)
|
||||
{
|
||||
DBG1(DBG_ESP, " IPsec SA: ESN not supported");
|
||||
return NULL;
|
||||
}
|
||||
if (ipcomp != IPCOMP_NONE)
|
||||
{
|
||||
DBG1(DBG_ESP, " IPsec SA: compression not supported");
|
||||
return NULL;
|
||||
}
|
||||
if (mode != MODE_TUNNEL)
|
||||
{
|
||||
DBG1(DBG_ESP, " IPsec SA: unsupported mode");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.destroy = _destroy,
|
||||
.get_source = _get_source,
|
||||
.get_destination = _get_destination,
|
||||
.get_spi = _get_spi,
|
||||
.get_reqid = _get_reqid,
|
||||
.get_protocol = _get_protocol,
|
||||
.get_lifetime = _get_lifetime,
|
||||
.is_inbound = _is_inbound,
|
||||
.match_by_spi_dst = _match_by_spi_dst,
|
||||
.match_by_spi_src_dst = _match_by_spi_src_dst,
|
||||
.match_by_reqid = _match_by_reqid,
|
||||
.get_esp_context = _get_esp_context,
|
||||
},
|
||||
.spi = spi,
|
||||
.src = src->clone(src),
|
||||
.dst = dst->clone(dst),
|
||||
.lifetime = *lifetime,
|
||||
.protocol = protocol,
|
||||
.reqid = reqid,
|
||||
.mode = mode,
|
||||
.esn = esn,
|
||||
.inbound = inbound,
|
||||
);
|
||||
|
||||
this->esp_context = esp_context_create(enc_alg, enc_key, int_alg, int_key,
|
||||
inbound);
|
||||
if (!this->esp_context)
|
||||
{
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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 <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 ipsec_sa ipsec_sa
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_SA_H_
|
||||
#define IPSEC_SA_H_
|
||||
|
||||
#include "esp_context.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/host.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
|
||||
typedef struct ipsec_sa_t ipsec_sa_t;
|
||||
|
||||
/**
|
||||
* IPsec Security Association (SA)
|
||||
*/
|
||||
struct ipsec_sa_t {
|
||||
|
||||
/**
|
||||
* Get the source address for this SA
|
||||
*
|
||||
* @return source address of this SA
|
||||
*/
|
||||
host_t *(*get_source)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Get the destination address for this SA
|
||||
*
|
||||
* @return destination address of this SA
|
||||
*/
|
||||
host_t *(*get_destination)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Get the SPI for this SA
|
||||
*
|
||||
* @return SPI of this SA
|
||||
*/
|
||||
u_int32_t (*get_spi)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Get the reqid of this SA
|
||||
*
|
||||
* @return reqid of this SA
|
||||
*/
|
||||
u_int32_t (*get_reqid)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Get the protocol (e.g. IPPROTO_ESP) of this SA
|
||||
*
|
||||
* @return protocol of this SA
|
||||
*/
|
||||
u_int8_t (*get_protocol)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Returns whether this SA is inbound or outbound
|
||||
*
|
||||
* @return TRUE if inbound, FALSE if outbound
|
||||
*/
|
||||
bool (*is_inbound)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Get the lifetime information for this SA
|
||||
* Note that this information is always relative to the time when the
|
||||
* SA was installed (i.e. it is not adjusted over time)
|
||||
*
|
||||
* @return lifetime of this SA
|
||||
*/
|
||||
lifetime_cfg_t *(*get_lifetime)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Get the ESP context for this SA
|
||||
*
|
||||
* @return ESP context of this SA
|
||||
*/
|
||||
esp_context_t *(*get_esp_context)(ipsec_sa_t *this);
|
||||
|
||||
/**
|
||||
* Check if this SA matches all given parameters
|
||||
*
|
||||
* @param spi SPI
|
||||
* @param dst destination address
|
||||
* @return TRUE if this SA matches all parameters, FALSE otherwise
|
||||
*/
|
||||
bool (*match_by_spi_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *dst);
|
||||
|
||||
/**
|
||||
* Check if this SA matches all given parameters
|
||||
*
|
||||
* @param spi SPI
|
||||
* @param src source address
|
||||
* @param dst destination address
|
||||
* @return TRUE if this SA matches all parameters, FALSE otherwise
|
||||
*/
|
||||
bool (*match_by_spi_src_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *src,
|
||||
host_t *dst);
|
||||
|
||||
/**
|
||||
* Check if this SA matches all given parameters
|
||||
*
|
||||
* @param reqid reqid
|
||||
* @param inbound TRUE for inbound SA, FALSE for outbound
|
||||
* @return TRUE if this SA matches all parameters, FALSE otherwise
|
||||
*/
|
||||
bool (*match_by_reqid)(ipsec_sa_t *this, u_int32_t reqid, bool inbound);
|
||||
|
||||
/**
|
||||
* Destroy an ipsec_sa_t
|
||||
*/
|
||||
void (*destroy)(ipsec_sa_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ipsec_sa_t instance
|
||||
*
|
||||
* @param spi SPI for this SA
|
||||
* @param src source address for this SA (gets cloned)
|
||||
* @param dst destination address for this SA (gets cloned)
|
||||
* @param protocol protocol for this SA (only ESP is supported)
|
||||
* @param reqid reqid for this SA
|
||||
* @param mark mark for this SA (ignored)
|
||||
* @param tfc Traffic Flow Confidentiality (currently not supported)
|
||||
* @param lifetime lifetime for this SA
|
||||
* @param enc_alg encryption algorithm for this SA
|
||||
* @param enc_key encryption key for this SA
|
||||
* @param int_alg integrity protection algorithm
|
||||
* @param int_key integrity protection key
|
||||
* @param mode mode for this SA (only tunnel mode is supported)
|
||||
* @param ipcomp IPcomp transform (not supported, use IPCOMP_NONE)
|
||||
* @param cpi CPI for IPcomp (ignored)
|
||||
* @param encap enable UDP encapsulation (must be TRUE)
|
||||
* @param esn Extended Sequence Numbers (currently not supported)
|
||||
* @param inbound TRUE if this is an inbound SA, FALSE otherwise
|
||||
* @param src_ts source traffic selector
|
||||
* @param dst_ts destination traffic selector
|
||||
* @return the IPsec SA, or NULL if the creation failed
|
||||
*/
|
||||
ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
|
||||
u_int8_t protocol, u_int32_t reqid, mark_t mark,
|
||||
u_int32_t tfc, lifetime_cfg_t *lifetime,
|
||||
u_int16_t enc_alg, chunk_t enc_key,
|
||||
u_int16_t int_alg, chunk_t int_key,
|
||||
ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
|
||||
bool encap, bool esn, bool inbound,
|
||||
traffic_selector_t *src_ts,
|
||||
traffic_selector_t *dst_ts);
|
||||
|
||||
#endif /** IPSEC_SA_H_ @}*/
|
|
@ -0,0 +1,626 @@
|
|||
/*
|
||||
* 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 <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 "ipsec.h"
|
||||
#include "ipsec_sa_mgr.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <library.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
#include <threading/condvar.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <utils/hashtable.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
|
||||
|
||||
/**
|
||||
* Private additions to ipsec_sa_mgr_t.
|
||||
*/
|
||||
struct private_ipsec_sa_mgr_t {
|
||||
|
||||
/**
|
||||
* Public members of ipsec_sa_mgr_t.
|
||||
*/
|
||||
ipsec_sa_mgr_t public;
|
||||
|
||||
/**
|
||||
* Installed SAs
|
||||
*/
|
||||
linked_list_t *sas;
|
||||
|
||||
/**
|
||||
* SPIs allocated using get_spi()
|
||||
*/
|
||||
hashtable_t *allocated_spis;
|
||||
|
||||
/**
|
||||
* Mutex used to synchronize access to the SA manager
|
||||
*/
|
||||
mutex_t *mutex;
|
||||
|
||||
/**
|
||||
* RNG used to generate SPIs
|
||||
*/
|
||||
rng_t *rng;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct to keep track of locked IPsec SAs
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
/**
|
||||
* IPsec SA
|
||||
*/
|
||||
ipsec_sa_t *sa;
|
||||
|
||||
/**
|
||||
* Set if this SA is currently in use by a thread
|
||||
*/
|
||||
bool locked;
|
||||
|
||||
/**
|
||||
* Condvar used by threads to wait for this entry
|
||||
*/
|
||||
condvar_t *condvar;
|
||||
|
||||
/**
|
||||
* Number of threads waiting for this entry
|
||||
*/
|
||||
u_int waiting_threads;
|
||||
|
||||
/**
|
||||
* Set if this entry is awaiting deletion
|
||||
*/
|
||||
bool awaits_deletion;
|
||||
|
||||
} ipsec_sa_entry_t;
|
||||
|
||||
/**
|
||||
* Helper struct for expiration events
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
/**
|
||||
* IPsec SA manager
|
||||
*/
|
||||
private_ipsec_sa_mgr_t *manager;
|
||||
|
||||
/**
|
||||
* Entry that expired
|
||||
*/
|
||||
ipsec_sa_entry_t *entry;
|
||||
|
||||
/**
|
||||
* 0 if this is a hard expire, otherwise the offset in s (soft->hard)
|
||||
*/
|
||||
u_int32_t hard_offset;
|
||||
|
||||
} ipsec_sa_expired_t;
|
||||
|
||||
/*
|
||||
* Used for the hash table of allocated SPIs
|
||||
*/
|
||||
static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi)
|
||||
{
|
||||
return *spi == *other_spi;
|
||||
}
|
||||
|
||||
static u_int spi_hash(u_int32_t *spi)
|
||||
{
|
||||
return chunk_hash(chunk_from_thing(*spi));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SA entry
|
||||
*/
|
||||
static ipsec_sa_entry_t *create_entry(ipsec_sa_t *sa)
|
||||
{
|
||||
ipsec_sa_entry_t *this;
|
||||
|
||||
INIT(this,
|
||||
.condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
|
||||
.sa = sa,
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an SA entry
|
||||
*/
|
||||
static void destroy_entry(ipsec_sa_entry_t *entry)
|
||||
{
|
||||
entry->condvar->destroy(entry->condvar);
|
||||
entry->sa->destroy(entry->sa);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure an entry is safe to remove
|
||||
* Must be called with this->mutex held.
|
||||
*
|
||||
* @return TRUE if entry can be removed, FALSE if entry is already
|
||||
* being removed by another thread
|
||||
*/
|
||||
static bool wait_remove_entry(private_ipsec_sa_mgr_t *this,
|
||||
ipsec_sa_entry_t *entry)
|
||||
{
|
||||
if (entry->awaits_deletion)
|
||||
{
|
||||
/* this will be deleted by another thread already */
|
||||
return FALSE;
|
||||
}
|
||||
entry->awaits_deletion = TRUE;
|
||||
while (entry->locked)
|
||||
{
|
||||
entry->condvar->wait(entry->condvar, this->mutex);
|
||||
}
|
||||
while (entry->waiting_threads > 0)
|
||||
{
|
||||
entry->condvar->broadcast(entry->condvar);
|
||||
entry->condvar->wait(entry->condvar, this->mutex);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until an is available and then locks it.
|
||||
* Must only be called with this->mutex held
|
||||
*/
|
||||
static bool wait_for_entry(private_ipsec_sa_mgr_t *this,
|
||||
ipsec_sa_entry_t *entry)
|
||||
{
|
||||
while (entry->locked && !entry->awaits_deletion)
|
||||
{
|
||||
entry->waiting_threads++;
|
||||
entry->condvar->wait(entry->condvar, this->mutex);
|
||||
entry->waiting_threads--;
|
||||
}
|
||||
if (entry->awaits_deletion)
|
||||
{
|
||||
/* others may still be waiting, */
|
||||
entry->condvar->signal(entry->condvar);
|
||||
return FALSE;
|
||||
}
|
||||
entry->locked = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes all entries
|
||||
* Must be called with this->mutex held.
|
||||
*/
|
||||
static void flush_entries(private_ipsec_sa_mgr_t *this)
|
||||
{
|
||||
ipsec_sa_entry_t *current;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
DBG2(DBG_ESP, "flushing SAD");
|
||||
|
||||
enumerator = this->sas->create_enumerator(this->sas);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
if (wait_remove_entry(this, current))
|
||||
{
|
||||
this->sas->remove_at(this->sas, enumerator);
|
||||
destroy_entry(current);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
/*
|
||||
* Different match functions to find SAs in the linked list
|
||||
*/
|
||||
static bool match_entry_by_ptr(ipsec_sa_entry_t *item, ipsec_sa_entry_t *entry)
|
||||
{
|
||||
return item == entry;
|
||||
}
|
||||
|
||||
static bool match_entry_by_sa_ptr(ipsec_sa_entry_t *item, ipsec_sa_t *sa)
|
||||
{
|
||||
return item->sa == sa;
|
||||
}
|
||||
|
||||
static bool match_entry_by_spi_inbound(ipsec_sa_entry_t *item, u_int32_t spi,
|
||||
bool inbound)
|
||||
{
|
||||
return item->sa->get_spi(item->sa) == spi &&
|
||||
item->sa->is_inbound(item->sa) == inbound;
|
||||
}
|
||||
|
||||
static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, u_int32_t spi,
|
||||
host_t *src, host_t *dst)
|
||||
{
|
||||
return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst);
|
||||
}
|
||||
|
||||
static bool match_entry_by_reqid_inbound(ipsec_sa_entry_t *item,
|
||||
u_int32_t reqid, bool inbound)
|
||||
{
|
||||
return item->sa->match_by_reqid(item->sa, reqid, inbound);
|
||||
}
|
||||
|
||||
static bool match_entry_by_spi_dst(ipsec_sa_entry_t *item, u_int32_t spi,
|
||||
host_t *dst)
|
||||
{
|
||||
return item->sa->match_by_spi_dst(item->sa, spi, dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry
|
||||
*/
|
||||
static bool remove_entry(private_ipsec_sa_mgr_t *this, ipsec_sa_entry_t *entry)
|
||||
{
|
||||
ipsec_sa_entry_t *current;
|
||||
enumerator_t *enumerator;
|
||||
bool removed = FALSE;
|
||||
|
||||
enumerator = this->sas->create_enumerator(this->sas);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
if (current == entry)
|
||||
{
|
||||
if (wait_remove_entry(this, current))
|
||||
{
|
||||
this->sas->remove_at(this->sas, enumerator);
|
||||
removed = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for expiration events
|
||||
*/
|
||||
static job_requeue_t sa_expired(ipsec_sa_expired_t *expired)
|
||||
{
|
||||
private_ipsec_sa_mgr_t *this = expired->manager;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->sas->find_first(this->sas, (void*)match_entry_by_ptr,
|
||||
NULL, expired->entry) == SUCCESS)
|
||||
{
|
||||
u_int32_t hard_offset = expired->hard_offset;
|
||||
ipsec_sa_t *sa = expired->entry->sa;
|
||||
|
||||
ipsec->events->expire(ipsec->events, sa->get_reqid(sa),
|
||||
sa->get_protocol(sa), sa->get_spi(sa),
|
||||
hard_offset == 0);
|
||||
if (hard_offset)
|
||||
{ /* soft limit reached, schedule hard expire */
|
||||
expired->hard_offset = 0;
|
||||
this->mutex->unlock(this->mutex);
|
||||
return JOB_RESCHEDULE(hard_offset);
|
||||
}
|
||||
/* hard limit reached */
|
||||
if (remove_entry(this, expired->entry))
|
||||
{
|
||||
destroy_entry(expired->entry);
|
||||
}
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
return JOB_REQUEUE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a job to handle IPsec SA expiration
|
||||
*/
|
||||
static void schedule_expiration(private_ipsec_sa_mgr_t *this,
|
||||
ipsec_sa_entry_t *entry)
|
||||
{
|
||||
lifetime_cfg_t *lifetime = entry->sa->get_lifetime(entry->sa);
|
||||
ipsec_sa_expired_t *expired;
|
||||
callback_job_t *job;
|
||||
u_int32_t timeout;
|
||||
|
||||
INIT(expired,
|
||||
.manager = this,
|
||||
.entry = entry,
|
||||
);
|
||||
|
||||
/* schedule a rekey first, a hard timeout will be scheduled then, if any */
|
||||
expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
|
||||
timeout = lifetime->time.rekey;
|
||||
|
||||
if (lifetime->time.life <= lifetime->time.rekey ||
|
||||
lifetime->time.rekey == 0)
|
||||
{ /* no rekey, schedule hard timeout */
|
||||
expired->hard_offset = 0;
|
||||
timeout = lifetime->time.life;
|
||||
}
|
||||
|
||||
job = callback_job_create((callback_job_cb_t)sa_expired, expired,
|
||||
(callback_job_cleanup_t)free, NULL);
|
||||
lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all allocated SPIs
|
||||
*/
|
||||
static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
u_int32_t *current;
|
||||
|
||||
DBG2(DBG_ESP, "flushing allocated SPIs");
|
||||
enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)¤t))
|
||||
{
|
||||
this->allocated_spis->remove_at(this->allocated_spis, enumerator);
|
||||
DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current));
|
||||
free(current);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-allocate an SPI for an inbound SA
|
||||
*/
|
||||
static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi)
|
||||
{
|
||||
u_int32_t *spi_alloc;
|
||||
|
||||
if (this->allocated_spis->get(this->allocated_spis, &spi) ||
|
||||
this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound,
|
||||
NULL, spi, TRUE) == SUCCESS)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
spi_alloc = malloc_thing(u_int32_t);
|
||||
*spi_alloc = spi;
|
||||
this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, get_spi, status_t,
|
||||
private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol,
|
||||
u_int32_t reqid, u_int32_t *spi)
|
||||
{
|
||||
u_int32_t spi_new;
|
||||
|
||||
DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (!this->rng)
|
||||
{
|
||||
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
if (!this->rng)
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1(DBG_ESP, "failed to create RNG for SPI generation");
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
|
||||
(u_int8_t*)&spi_new))
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid);
|
||||
return FAILED;
|
||||
}
|
||||
/* make sure the SPI is valid (not in range 0-255) */
|
||||
spi_new |= 0x00000100;
|
||||
spi_new = htonl(spi_new);
|
||||
}
|
||||
while (!allocate_spi(this, spi_new));
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
*spi = spi_new;
|
||||
|
||||
DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, add_sa, status_t,
|
||||
private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
|
||||
u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc,
|
||||
lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
|
||||
u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
|
||||
u_int16_t cpi, bool encap, bool esn, bool inbound,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
|
||||
{
|
||||
ipsec_sa_entry_t *entry;
|
||||
ipsec_sa_t *sa_new;
|
||||
|
||||
DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
|
||||
ntohl(spi), reqid);
|
||||
DBG2(DBG_ESP, " using encryption algorithm %N with key size %d",
|
||||
encryption_algorithm_names, enc_alg, enc_key.len * 8);
|
||||
DBG2(DBG_ESP, " using integrity algorithm %N with key size %d",
|
||||
integrity_algorithm_names, int_alg, int_key.len * 8);
|
||||
|
||||
sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
|
||||
lifetime, enc_alg, enc_key, int_alg, int_key, mode,
|
||||
ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts);
|
||||
if (!sa_new)
|
||||
{
|
||||
DBG1(DBG_ESP, "failed to create SAD entry");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
|
||||
if (inbound)
|
||||
{ /* remove any pre-allocated SPIs */
|
||||
u_int32_t *spi_alloc;
|
||||
|
||||
spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
|
||||
free(spi_alloc);
|
||||
}
|
||||
|
||||
if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
|
||||
NULL, spi, src, dst) == SUCCESS)
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
DBG1(DBG_ESP, "failed to install SAD entry: already installed");
|
||||
sa_new->destroy(sa_new);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
entry = create_entry(sa_new);
|
||||
schedule_expiration(this, entry);
|
||||
this->sas->insert_last(this->sas, entry);
|
||||
|
||||
this->mutex->unlock(this->mutex);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, del_sa, status_t,
|
||||
private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
|
||||
u_int8_t protocol, u_int16_t cpi, mark_t mark)
|
||||
{
|
||||
ipsec_sa_entry_t *current, *found = NULL;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
enumerator = this->sas->create_enumerator(this->sas);
|
||||
while (enumerator->enumerate(enumerator, (void**)¤t))
|
||||
{
|
||||
if (match_entry_by_spi_src_dst(current, spi, src, dst))
|
||||
{
|
||||
if (wait_remove_entry(this, current))
|
||||
{
|
||||
this->sas->remove_at(this->sas, enumerator);
|
||||
found = current;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
if (found)
|
||||
{
|
||||
DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
|
||||
found->sa->is_inbound(found->sa) ? "in" : "out", ntohl(spi));
|
||||
destroy_entry(found);
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
|
||||
private_ipsec_sa_mgr_t *this, u_int32_t reqid, bool inbound)
|
||||
{
|
||||
ipsec_sa_entry_t *entry;
|
||||
ipsec_sa_t *sa = NULL;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->sas->find_first(this->sas, (void*)match_entry_by_reqid_inbound,
|
||||
(void**)&entry, reqid, inbound) == SUCCESS &&
|
||||
wait_for_entry(this, entry))
|
||||
{
|
||||
sa = entry->sa;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
return sa;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*,
|
||||
private_ipsec_sa_mgr_t *this, u_int32_t spi, host_t *dst)
|
||||
{
|
||||
ipsec_sa_entry_t *entry;
|
||||
ipsec_sa_t *sa = NULL;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_dst,
|
||||
(void**)&entry, spi, dst) == SUCCESS &&
|
||||
wait_for_entry(this, entry))
|
||||
{
|
||||
sa = entry->sa;
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
return sa;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, checkin, void,
|
||||
private_ipsec_sa_mgr_t *this, ipsec_sa_t *sa)
|
||||
{
|
||||
ipsec_sa_entry_t *entry;
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (this->sas->find_first(this->sas, (void*)match_entry_by_sa_ptr,
|
||||
(void**)&entry, sa) == SUCCESS)
|
||||
{
|
||||
if (entry->locked)
|
||||
{
|
||||
entry->locked = FALSE;
|
||||
entry->condvar->signal(entry->condvar);
|
||||
}
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
|
||||
private_ipsec_sa_mgr_t *this)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
flush_entries(this);
|
||||
this->mutex->unlock(this->mutex);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
METHOD(ipsec_sa_mgr_t, destroy, void,
|
||||
private_ipsec_sa_mgr_t *this)
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
flush_entries(this);
|
||||
flush_allocated_spis(this);
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
this->allocated_spis->destroy(this->allocated_spis);
|
||||
this->sas->destroy(this->sas);
|
||||
|
||||
this->mutex->destroy(this->mutex);
|
||||
DESTROY_IF(this->rng);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
ipsec_sa_mgr_t *ipsec_sa_mgr_create()
|
||||
{
|
||||
private_ipsec_sa_mgr_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_spi = _get_spi,
|
||||
.add_sa = _add_sa,
|
||||
.del_sa = _del_sa,
|
||||
.checkout_by_spi = _checkout_by_spi,
|
||||
.checkout_by_reqid = _checkout_by_reqid,
|
||||
.checkin = _checkin,
|
||||
.flush_sas = _flush_sas,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.sas = linked_list_create(),
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
|
||||
(hashtable_equals_t)spi_equals, 16),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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 <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 ipsec_sa_mgr ipsec_sa_mgr
|
||||
* @{ @ingroup libipsec
|
||||
*/
|
||||
|
||||
#ifndef IPSEC_SA_MGR_H_
|
||||
#define IPSEC_SA_MGR_H_
|
||||
|
||||
#include "ipsec_sa.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <ipsec/ipsec_types.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
#include <utils/host.h>
|
||||
|
||||
typedef struct ipsec_sa_mgr_t ipsec_sa_mgr_t;
|
||||
|
||||
/**
|
||||
* IPsec SA manager
|
||||
*
|
||||
* The first methods are modeled after those in kernel_ipsec_t.
|
||||
*/
|
||||
struct ipsec_sa_mgr_t {
|
||||
|
||||
/**
|
||||
* Allocate an SPI for an inbound IPsec SA
|
||||
*
|
||||
* @param src source address of the SA
|
||||
* @param dst destination address of the SA
|
||||
* @param protocol protocol of the SA (only ESP supported)
|
||||
* @param reqid reqid for the SA
|
||||
* @param spi the allocated SPI
|
||||
* @return SUCCESS of operation successful
|
||||
*/
|
||||
status_t (*get_spi)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
|
||||
u_int8_t protocol, u_int32_t reqid, u_int32_t *spi);
|
||||
|
||||
/**
|
||||
* Add a new SA
|
||||
*
|
||||
* @param src source address for this SA (gets cloned)
|
||||
* @param dst destination address for this SA (gets cloned)
|
||||
* @param spi SPI for this SA
|
||||
* @param protocol protocol for this SA (only ESP is supported)
|
||||
* @param reqid reqid for this SA
|
||||
* @param mark mark for this SA (ignored)
|
||||
* @param tfc Traffic Flow Confidentiality (not yet supported)
|
||||
* @param lifetime lifetime for this SA
|
||||
* @param enc_alg encryption algorithm for this SA
|
||||
* @param enc_key encryption key for this SA
|
||||
* @param int_alg integrity protection algorithm
|
||||
* @param int_key integrity protection key
|
||||
* @param mode mode for this SA (only tunnel mode is supported)
|
||||
* @param ipcomp IPcomp transform (not supported, use IPCOMP_NONE)
|
||||
* @param cpi CPI for IPcomp (ignored)
|
||||
* @param encap enable UDP encapsulation (must be TRUE)
|
||||
* @param esn Extended Sequence Numbers (currently not supported)
|
||||
* @param inbound TRUE if this is an inbound SA, FALSE otherwise
|
||||
* @param src_ts source traffic selector
|
||||
* @param dst_ts destination traffic selector
|
||||
* @return SUCCESS if operation completed
|
||||
*/
|
||||
status_t (*add_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
|
||||
u_int32_t spi, u_int8_t protocol, u_int32_t reqid,
|
||||
mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime,
|
||||
u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg,
|
||||
chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
|
||||
u_int16_t cpi, bool encap, bool esn, bool inbound,
|
||||
traffic_selector_t *src_ts, traffic_selector_t *dst_ts);
|
||||
|
||||
/**
|
||||
* Delete a previously added SA
|
||||
*
|
||||
* @param spi SPI of the SA
|
||||
* @param src source address of the SA
|
||||
* @param dst destination address of the SA
|
||||
* @param protocol protocol of the SA
|
||||
* @param cpi CPI for IPcomp
|
||||
* @param mark optional mark
|
||||
* @return SUCCESS if operation completed
|
||||
*/
|
||||
status_t (*del_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
|
||||
u_int32_t spi, u_int8_t protocol, u_int16_t cpi,
|
||||
mark_t mark);
|
||||
|
||||
/**
|
||||
* Flush all SAs
|
||||
*
|
||||
* @return SUCCESS if operation completed
|
||||
*/
|
||||
status_t (*flush_sas)(ipsec_sa_mgr_t *this);
|
||||
|
||||
/**
|
||||
* Checkout an installed IPsec SA by SPI and destination address
|
||||
* Can be used to find the correct SA for an inbound packet.
|
||||
*
|
||||
* The matching SA is locked until it is checked in using checkin().
|
||||
* If the matching SA is already checked out, this call blocks until the
|
||||
* SA is checked in.
|
||||
*
|
||||
* Since other threads may be waiting for the checked out SA, it should be
|
||||
* checked in as soon as possible after use.
|
||||
*
|
||||
* @param spi SPI (e.g. of an inbound packet)
|
||||
* @param dst destination address (e.g. of an inbound packet)
|
||||
* @return the matching IPsec SA, or NULL if none is found
|
||||
*/
|
||||
ipsec_sa_t *(*checkout_by_spi)(ipsec_sa_mgr_t *this, u_int32_t spi,
|
||||
host_t *dst);
|
||||
|
||||
/**
|
||||
* Checkout an installed IPsec SA by its reqid and inbound/outbound flag.
|
||||
* Can be used to find the correct SA for an outbound packet.
|
||||
*
|
||||
* The matching SA is locked until it is checked in using checkin().
|
||||
* If the matching SA is already checked out, this call blocks until the
|
||||
* SA is checked in.
|
||||
*
|
||||
* Since other threads may be waiting for a checked out SA, it should be
|
||||
* checked in as soon as possible after use.
|
||||
*
|
||||
* @param reqid reqid of the SA
|
||||
* @param inbound TRUE for an inbound SA, FALSE for an outbound SA
|
||||
* @return the matching IPsec SA, or NULL if none is found
|
||||
*/
|
||||
ipsec_sa_t *(*checkout_by_reqid)(ipsec_sa_mgr_t *this, u_int32_t reqid,
|
||||
bool inbound);
|
||||
|
||||
/**
|
||||
* Checkin an SA after use.
|
||||
*
|
||||
* @param sa checked out SA
|
||||
*/
|
||||
void (*checkin)(ipsec_sa_mgr_t *this, ipsec_sa_t *sa);
|
||||
|
||||
/**
|
||||
* Destroy an ipsec_sa_mgr_t
|
||||
*/
|
||||
void (*destroy)(ipsec_sa_mgr_t *this);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ipsec_sa_mgr instance
|
||||
*
|
||||
* @return IPsec SA manager instance
|
||||
*/
|
||||
ipsec_sa_mgr_t *ipsec_sa_mgr_create();
|
||||
|
||||
#endif /** IPSEC_SA_MGR_H_ @}*/
|
|
@ -20,13 +20,14 @@ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \
|
|||
credentials/sets/cert_cache.c credentials/sets/mem_cred.c \
|
||||
credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \
|
||||
database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap.c \
|
||||
ipsec/ipsec_types.c \
|
||||
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
|
||||
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
|
||||
selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
|
||||
threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
|
||||
utils.c utils/host.c utils/identification.c utils/lexparser.c \
|
||||
utils/linked_list.c utils/hashtable.c utils/enumerator.c utils/optionsfrom.c \
|
||||
utils/capabilities.c utils/backtrace.c
|
||||
utils.c utils/host.c utils/packet.c utils/identification.c utils/lexparser.c \
|
||||
utils/linked_list.c utils/blocking_queue.c utils/hashtable.c utils/enumerator.c \
|
||||
utils/optionsfrom.c utils/capabilities.c utils/backtrace.c utils/tun_device.c
|
||||
|
||||
# adding the plugin source files
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \
|
|||
credentials/sets/cert_cache.c credentials/sets/mem_cred.c \
|
||||
credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \
|
||||
database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap.c \
|
||||
ipsec/ipsec_types.c \
|
||||
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
|
||||
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
|
||||
selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
|
||||
threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
|
||||
utils.c utils/host.c utils/identification.c utils/lexparser.c \
|
||||
utils/linked_list.c utils/hashtable.c utils/enumerator.c utils/optionsfrom.c \
|
||||
utils/capabilities.c utils/backtrace.c
|
||||
utils.c utils/host.c utils/packet.c utils/identification.c utils/lexparser.c \
|
||||
utils/linked_list.c utils/blocking_queue.c utils/hashtable.c utils/enumerator.c \
|
||||
utils/optionsfrom.c utils/capabilities.c utils/backtrace.c utils/tun_device.c
|
||||
|
||||
if USE_DEV_HEADERS
|
||||
strongswan_includedir = ${dev_headers}
|
||||
|
@ -51,14 +52,16 @@ credentials/sets/ocsp_response_wrapper.h credentials/sets/cert_cache.h \
|
|||
credentials/sets/mem_cred.h credentials/sets/callback_cred.h \
|
||||
credentials/auth_cfg.h credentials/credential_set.h credentials/cert_validator.h \
|
||||
database/database.h database/database_factory.h fetcher/fetcher.h \
|
||||
fetcher/fetcher_manager.h eap/eap.h pen/pen.h plugins/plugin_loader.h \
|
||||
plugins/plugin.h plugins/plugin_feature.h processing/jobs/job.h \
|
||||
processing/jobs/callback_job.h processing/processor.h processing/scheduler.h \
|
||||
selectors/traffic_selector.h threading/thread.h threading/thread_value.h \
|
||||
fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \
|
||||
plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h
|
||||
processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h
|
||||
processing/scheduler.h selectors/traffic_selector.h \
|
||||
threading/thread.h threading/thread_value.h \
|
||||
threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
|
||||
threading/rwlock.h threading/lock_profiler.h utils.h utils/host.h \
|
||||
utils/identification.h utils/lexparser.h utils/linked_list.h utils/hashtable.h \
|
||||
utils/enumerator.h utils/optionsfrom.h utils/capabilities.h utils/backtrace.h
|
||||
utils/packet.h utils/identification.h utils/lexparser.h utils/linked_list.h \
|
||||
utils/blocking_queue.h utils/hashtable.h utils/enumerator.h utils/optionsfrom.h \
|
||||
utils/capabilities.h utils/backtrace.h utils/tun_device.h
|
||||
endif
|
||||
|
||||
library.lo : $(top_builddir)/config.status
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Copyright (C) 2010 revosec AG
|
||||
*
|
||||
|
@ -47,8 +50,38 @@ METHOD(bio_reader_t, peek, chunk_t,
|
|||
return this->buf;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint8, bool,
|
||||
private_bio_reader_t *this, u_int8_t *res)
|
||||
/**
|
||||
* A version of chunk_skip() that supports skipping from the end (i.e. simply
|
||||
* reducing the size)
|
||||
*/
|
||||
static inline chunk_t chunk_skip_end(chunk_t chunk, size_t bytes, bool from_end)
|
||||
{
|
||||
if (chunk.len > bytes)
|
||||
{
|
||||
if (!from_end)
|
||||
{
|
||||
chunk.ptr += bytes;
|
||||
}
|
||||
chunk.len -= bytes;
|
||||
return chunk;
|
||||
}
|
||||
return chunk_empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the data to read, optionally from the end
|
||||
*/
|
||||
static inline u_char *get_ptr_end(private_bio_reader_t *this, u_int32_t len,
|
||||
bool from_end)
|
||||
{
|
||||
return from_end ? this->buf.ptr + (this->buf.len - len) : this->buf.ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an u_int8_t from the buffer, optionally from the end of the buffer
|
||||
*/
|
||||
static bool read_uint8_internal(private_bio_reader_t *this, u_int8_t *res,
|
||||
bool from_end)
|
||||
{
|
||||
if (this->buf.len < 1)
|
||||
{
|
||||
|
@ -56,13 +89,16 @@ METHOD(bio_reader_t, read_uint8, bool,
|
|||
this->buf.len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = this->buf.ptr[0];
|
||||
this->buf = chunk_skip(this->buf, 1);
|
||||
*res = *get_ptr_end(this, 1, from_end);
|
||||
this->buf = chunk_skip_end(this->buf, 1, from_end);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint16, bool,
|
||||
private_bio_reader_t *this, u_int16_t *res)
|
||||
/**
|
||||
* Read an u_int16_t from the buffer, optionally from the end
|
||||
*/
|
||||
static bool read_uint16_internal(private_bio_reader_t *this, u_int16_t *res,
|
||||
bool from_end)
|
||||
{
|
||||
if (this->buf.len < 2)
|
||||
{
|
||||
|
@ -70,13 +106,16 @@ METHOD(bio_reader_t, read_uint16, bool,
|
|||
this->buf.len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh16(this->buf.ptr);
|
||||
this->buf = chunk_skip(this->buf, 2);
|
||||
*res = untoh16(get_ptr_end(this, 2, from_end));
|
||||
this->buf = chunk_skip_end(this->buf, 2, from_end);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint24, bool,
|
||||
private_bio_reader_t *this, u_int32_t *res)
|
||||
/**
|
||||
* Read an u_int32_t (only 24-bit) from the buffer, optionally from the end
|
||||
*/
|
||||
static bool read_uint24_internal(private_bio_reader_t *this, u_int32_t *res,
|
||||
bool from_end)
|
||||
{
|
||||
if (this->buf.len < 3)
|
||||
{
|
||||
|
@ -84,13 +123,16 @@ METHOD(bio_reader_t, read_uint24, bool,
|
|||
this->buf.len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh32(this->buf.ptr) >> 8;
|
||||
this->buf = chunk_skip(this->buf, 3);
|
||||
*res = untoh32(get_ptr_end(this, 3, from_end)) >> 8;
|
||||
this->buf = chunk_skip_end(this->buf, 3, from_end);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint32, bool,
|
||||
private_bio_reader_t *this, u_int32_t *res)
|
||||
/**
|
||||
* Read an u_int32_t from the buffer, optionally from the end
|
||||
*/
|
||||
static bool read_uint32_internal(private_bio_reader_t *this, u_int32_t *res,
|
||||
bool from_end)
|
||||
{
|
||||
if (this->buf.len < 4)
|
||||
{
|
||||
|
@ -98,13 +140,16 @@ METHOD(bio_reader_t, read_uint32, bool,
|
|||
this->buf.len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh32(this->buf.ptr);
|
||||
this->buf = chunk_skip(this->buf, 4);
|
||||
*res = untoh32(get_ptr_end(this, 4, from_end));
|
||||
this->buf = chunk_skip_end(this->buf, 4, from_end);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint64, bool,
|
||||
private_bio_reader_t *this, u_int64_t *res)
|
||||
/**
|
||||
* Read an u_int64_t from the buffer, optionally from the end
|
||||
*/
|
||||
static bool read_uint64_internal(private_bio_reader_t *this, u_int64_t *res,
|
||||
bool from_end)
|
||||
{
|
||||
if (this->buf.len < 8)
|
||||
{
|
||||
|
@ -112,13 +157,16 @@ METHOD(bio_reader_t, read_uint64, bool,
|
|||
this->buf.len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = untoh64(this->buf.ptr);
|
||||
this->buf = chunk_skip(this->buf, 8);
|
||||
*res = untoh64(get_ptr_end(this, 8, from_end));
|
||||
this->buf = chunk_skip_end(this->buf, 8, from_end);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_data, bool,
|
||||
private_bio_reader_t *this, u_int32_t len, chunk_t *res)
|
||||
/**
|
||||
* Read a chunk of data from the buffer, optionally from the end
|
||||
*/
|
||||
static bool read_data_internal(private_bio_reader_t *this, u_int32_t len,
|
||||
chunk_t *res, bool from_end)
|
||||
{
|
||||
if (this->buf.len < len)
|
||||
{
|
||||
|
@ -126,11 +174,83 @@ METHOD(bio_reader_t, read_data, bool,
|
|||
this->buf.len, len);
|
||||
return FALSE;
|
||||
}
|
||||
*res = chunk_create(this->buf.ptr, len);
|
||||
this->buf = chunk_skip(this->buf, len);
|
||||
*res = chunk_create(get_ptr_end(this, len, from_end), len);
|
||||
this->buf = chunk_skip_end(this->buf, len, from_end);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint8, bool,
|
||||
private_bio_reader_t *this, u_int8_t *res)
|
||||
{
|
||||
return read_uint8_internal(this, res, FALSE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint16, bool,
|
||||
private_bio_reader_t *this, u_int16_t *res)
|
||||
{
|
||||
return read_uint16_internal(this, res, FALSE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint24, bool,
|
||||
private_bio_reader_t *this, u_int32_t *res)
|
||||
{
|
||||
return read_uint24_internal(this, res, FALSE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint32, bool,
|
||||
private_bio_reader_t *this, u_int32_t *res)
|
||||
{
|
||||
return read_uint32_internal(this, res, FALSE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint64, bool,
|
||||
private_bio_reader_t *this, u_int64_t *res)
|
||||
{
|
||||
return read_uint64_internal(this, res, FALSE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_data, bool,
|
||||
private_bio_reader_t *this, u_int32_t len, chunk_t *res)
|
||||
{
|
||||
return read_data_internal(this, len, res, FALSE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint8_end, bool,
|
||||
private_bio_reader_t *this, u_int8_t *res)
|
||||
{
|
||||
return read_uint8_internal(this, res, TRUE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint16_end, bool,
|
||||
private_bio_reader_t *this, u_int16_t *res)
|
||||
{
|
||||
return read_uint16_internal(this, res, TRUE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint24_end, bool,
|
||||
private_bio_reader_t *this, u_int32_t *res)
|
||||
{
|
||||
return read_uint24_internal(this, res, TRUE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint32_end, bool,
|
||||
private_bio_reader_t *this, u_int32_t *res)
|
||||
{
|
||||
return read_uint32_internal(this, res, TRUE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_uint64_end, bool,
|
||||
private_bio_reader_t *this, u_int64_t *res)
|
||||
{
|
||||
return read_uint64_internal(this, res, TRUE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_data_end, bool,
|
||||
private_bio_reader_t *this, u_int32_t len, chunk_t *res)
|
||||
{
|
||||
return read_data_internal(this, len, res, TRUE);
|
||||
}
|
||||
|
||||
METHOD(bio_reader_t, read_data8, bool,
|
||||
private_bio_reader_t *this, chunk_t *res)
|
||||
{
|
||||
|
@ -202,6 +322,12 @@ bio_reader_t *bio_reader_create(chunk_t data)
|
|||
.read_uint32 = _read_uint32,
|
||||
.read_uint64 = _read_uint64,
|
||||
.read_data = _read_data,
|
||||
.read_uint8_end = _read_uint8_end,
|
||||
.read_uint16_end = _read_uint16_end,
|
||||
.read_uint24_end = _read_uint24_end,
|
||||
.read_uint32_end = _read_uint32_end,
|
||||
.read_uint64_end = _read_uint64_end,
|
||||
.read_data_end = _read_data_end,
|
||||
.read_data8 = _read_data8,
|
||||
.read_data16 = _read_data16,
|
||||
.read_data24 = _read_data24,
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Tobias Brunner
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
* Copyright (C) 2010 revosec AG
|
||||
*
|
||||
|
@ -27,6 +30,8 @@ typedef struct bio_reader_t bio_reader_t;
|
|||
|
||||
/**
|
||||
* Buffered input parser.
|
||||
*
|
||||
* @note Integers are returned in host byte order.
|
||||
*/
|
||||
struct bio_reader_t {
|
||||
|
||||
|
@ -93,6 +98,55 @@ struct bio_reader_t {
|
|||
*/
|
||||
bool (*read_data)(bio_reader_t *this, u_int32_t len, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Read a 8-bit integer from the end of the buffer, reduce remaining.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint8_end)(bio_reader_t *this, u_int8_t *res);
|
||||
|
||||
/**
|
||||
* Read a 16-bit integer from the end of the buffer, reduce remaining.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint16_end)(bio_reader_t *this, u_int16_t *res);
|
||||
|
||||
/**
|
||||
* Read a 24-bit integer from the end of the buffer, reduce remaining.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint24_end)(bio_reader_t *this, u_int32_t *res);
|
||||
|
||||
/**
|
||||
* Read a 32-bit integer from the end of the buffer, reduce remaining.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint32_end)(bio_reader_t *this, u_int32_t *res);
|
||||
|
||||
/**
|
||||
* Read a 64-bit integer from the end of the buffer, reduce remaining.
|
||||
*
|
||||
* @param res pointer to result
|
||||
* @return TRUE if integer read successfully
|
||||
*/
|
||||
bool (*read_uint64_end)(bio_reader_t *this, u_int64_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of len bytes from the end of the buffer, reduce remaining.
|
||||
*
|
||||
* @param len number of bytes to read
|
||||
* @param res ponter to result, not cloned
|
||||
* @return TRUE if data read successfully
|
||||
*/
|
||||
bool (*read_data_end)(bio_reader_t *this, u_int32_t len, chunk_t *res);
|
||||
|
||||
/**
|
||||
* Read a chunk of bytes with a 8-bit length header, advance.
|
||||
*
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue