aboutsummaryrefslogtreecommitdiffstats
path: root/src/frontends/android/jni/libandroidbridge
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontends/android/jni/libandroidbridge')
-rw-r--r--src/frontends/android/jni/libandroidbridge/android_jni.h13
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_creds.c85
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_creds.h7
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c72
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.h8
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.c88
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.h11
7 files changed, 244 insertions, 40 deletions
diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.h b/src/frontends/android/jni/libandroidbridge/android_jni.h
index 774d37d7e..bafd6b72e 100644
--- a/src/frontends/android/jni/libandroidbridge/android_jni.h
+++ b/src/frontends/android/jni/libandroidbridge/android_jni.h
@@ -90,13 +90,16 @@ static inline bool androidjni_exception_occurred(JNIEnv *env)
*/
static inline char *androidjni_convert_jstring(JNIEnv *env, jstring jstr)
{
- char *str;
+ char *str = NULL;
jsize len;
- len = (*env)->GetStringUTFLength(env, jstr);
- str = malloc(len + 1);
- (*env)->GetStringUTFRegion(env, jstr, 0, len, str);
- str[len] = '\0';
+ if (jstr)
+ {
+ len = (*env)->GetStringUTFLength(env, jstr);
+ str = malloc(len + 1);
+ (*env)->GetStringUTFRegion(env, jstr, 0, len, str);
+ str[len] = '\0';
+ }
return str;
}
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c
index 27023d721..931f22316 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_creds.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.c
@@ -50,6 +50,15 @@ struct private_android_creds_t {
};
/**
+ * Free allocated DER encoding
+ */
+static void free_encoding(chunk_t *chunk)
+{
+ chunk_free(chunk);
+ free(chunk);
+}
+
+/**
* Load trusted certificates via charonservice (JNI).
*/
static void load_trusted_certificates(private_android_creds_t *this)
@@ -71,8 +80,7 @@ static void load_trusted_certificates(private_android_creds_t *this)
cert->get_subject(cert));
this->creds->add_cert(this->creds, TRUE, cert);
}
- chunk_free(current);
- free(current);
+ free_encoding(current);
}
certs->destroy(certs);
}
@@ -130,6 +138,76 @@ METHOD(credential_set_t, create_shared_enumerator, enumerator_t*,
type, me, other);
}
+METHOD(android_creds_t, load_user_certificate, certificate_t*,
+ private_android_creds_t *this)
+{
+ linked_list_t *encodings;
+ certificate_t *cert = NULL, *ca_cert;
+ private_key_t *key = NULL;
+ chunk_t *current;
+
+ encodings = charonservice->get_user_certificate(charonservice);
+ if (!encodings)
+ {
+ return NULL;
+ }
+
+ while (encodings->remove_first(encodings, (void**)&current) == SUCCESS)
+ {
+ if (!key)
+ { /* the first element is the private key, we assume RSA */
+ key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
+ BUILD_BLOB_ASN1_DER, *current, BUILD_END);
+ if (key)
+ {
+ this->creds->add_key(this->creds, key);
+ free_encoding(current);
+ continue;
+ }
+ goto failed;
+ }
+ if (!cert)
+ { /* the next element is the user certificate */
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB_ASN1_DER, *current, BUILD_END);
+ if (cert)
+ {
+ DBG1(DBG_CFG, "loaded user certificate '%Y' and private key",
+ cert->get_subject(cert));
+ cert = this->creds->add_cert_ref(this->creds, TRUE, cert);
+ free_encoding(current);
+ continue;
+ }
+ goto failed;
+ }
+ /* the rest are CA certificates, we ignore failures */
+ ca_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB_ASN1_DER, *current, BUILD_END);
+ if (ca_cert)
+ {
+ DBG1(DBG_CFG, "loaded CA certificate '%Y'",
+ ca_cert->get_subject(ca_cert));
+ this->creds->add_cert(this->creds, TRUE, ca_cert);
+ }
+ free_encoding(current);
+ }
+ encodings->destroy(encodings);
+ return cert;
+
+failed:
+ DBG1(DBG_CFG, "failed to load user certificate and private key");
+ free_encoding(current);
+ encodings->destroy_function(encodings, (void*)free_encoding);
+ return NULL;
+}
+
+METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
+ private_android_creds_t *this, key_type_t type, identification_t *id)
+{
+ return this->creds->set.create_private_enumerator(&this->creds->set,
+ type, id);
+}
+
METHOD(android_creds_t, clear, void,
private_android_creds_t *this)
{
@@ -160,11 +238,12 @@ android_creds_t *android_creds_create()
.set = {
.create_cert_enumerator = _create_cert_enumerator,
.create_shared_enumerator = _create_shared_enumerator,
- .create_private_enumerator = (void*)return_null,
+ .create_private_enumerator = _create_private_enumerator,
.create_cdp_enumerator = (void*)return_null,
.cache_cert = (void*)nop,
},
.add_username_password = _add_username_password,
+ .load_user_certificate = _load_user_certificate,
.clear = _clear,
.destroy = _destroy,
},
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_creds.h b/src/frontends/android/jni/libandroidbridge/backend/android_creds.h
index 33de838c1..a3ecddde4 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_creds.h
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_creds.h
@@ -47,6 +47,13 @@ struct android_creds_t {
char *password);
/**
+ * Load the user certificate and private key
+ *
+ * @preturn loaded client certificate, NULL on failure
+ */
+ certificate_t *(*load_user_certificate)(android_creds_t *this);
+
+ /**
* Clear the cached certificates and stored credentials.
*/
void (*clear)(android_creds_t *this);
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
index d1769a99a..f62aea0e8 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
@@ -44,11 +44,21 @@ struct private_android_service_t {
android_service_t public;
/**
+ * credential set
+ */
+ android_creds_t *creds;
+
+ /**
* current IKE_SA
*/
ike_sa_t *ike_sa;
/**
+ * the type of VPN
+ */
+ char *type;
+
+ /**
* local ipv4 address
*/
char *local_address;
@@ -64,6 +74,11 @@ struct private_android_service_t {
char *username;
/**
+ * password
+ */
+ char *password;
+
+ /**
* lock to safely access the TUN device fd
*/
rwlock_t *lock;
@@ -445,11 +460,42 @@ static job_requeue_t initiate(private_android_service_t *this)
FALSE, NULL, NULL); /* mediation */
peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
- 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);
+ /* local auth config */
+ if (streq("ikev2-eap", this->type))
+ {
+ 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);
+
+ this->creds->add_username_password(this->creds, this->username,
+ this->password);
+ memwipe(this->password, strlen(this->password));
+ peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
+ }
+ else if (streq("ikev2-cert", this->type))
+ {
+ certificate_t *cert;
+ identification_t *id;
+
+ cert = this->creds->load_user_certificate(this->creds);
+ if (!cert)
+ {
+ peer_cfg->destroy(peer_cfg);
+ charonservice->update_status(charonservice,
+ CHARONSERVICE_GENERIC_ERROR);
+ return JOB_REQUEUE_NONE;
+
+ }
+ auth = auth_cfg_create();
+ auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+ auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert);
+ id = cert->get_subject(cert);
+ auth->add(auth, AUTH_RULE_IDENTITY, id->clone(id));
+ peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
+ }
+
+ /* remote auth config */
auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
gateway = identification_create_from_string(this->gateway);
@@ -506,17 +552,24 @@ METHOD(android_service_t, destroy, void,
/* make sure the tun device is actually closed */
close_tun_device(this);
this->lock->destroy(this->lock);
+ free(this->type);
free(this->local_address);
- free(this->username);
free(this->gateway);
+ free(this->username);
+ if (this->password)
+ {
+ memwipe(this->password, strlen(this->password));
+ free(this->password);
+ }
free(this);
}
/**
* See header
*/
-android_service_t *android_service_create(char *local_address, char *gateway,
- char *username)
+android_service_t *android_service_create(android_creds_t *creds, char *type,
+ char *local_address, char *gateway,
+ char *username, char *password)
{
private_android_service_t *this;
@@ -534,7 +587,10 @@ android_service_t *android_service_create(char *local_address, char *gateway,
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
.local_address = local_address,
.username = username,
+ .password = password,
.gateway = gateway,
+ .creds = creds,
+ .type = type,
.tunfd = -1,
);
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
index a7bd8b059..52c3dc5c8 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.h
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
@@ -51,11 +51,15 @@ struct android_service_t {
* Create an Android service instance. Queues a job that starts initiation of a
* new IKE SA.
*
+ * @param creds Android specific credential set
+ * @param type VPN type (see VpnType.java)
* @param local_address local ip address
* @param gateway gateway address
* @param username user name (local identity)
+ * @param password password (if any)
*/
-android_service_t *android_service_create(char *local_address, char *gateway,
- char *username);
+android_service_t *android_service_create(android_creds_t *creds, char *type,
+ char *local_address, char *gateway,
+ char *username, char *password);
#endif /** ANDROID_SERVICE_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c
index fab99ac10..59ec62fc7 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.c
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.c
@@ -199,6 +199,33 @@ failed:
return FALSE;
}
+/**
+ * Converts the given Java array of byte arrays (byte[][]) to a linked list
+ * of chunk_t objects.
+ */
+static linked_list_t *convert_array_of_byte_arrays(JNIEnv *env,
+ jobjectArray jarray)
+{
+ linked_list_t *list;
+ jsize i;
+
+ list = linked_list_create();
+ for (i = 0; i < (*env)->GetArrayLength(env, jarray); ++i)
+ {
+ chunk_t *chunk;
+ jbyteArray jbytearray;
+
+ chunk = malloc_thing(chunk_t);
+ list->insert_last(list, chunk);
+
+ jbytearray = (*env)->GetObjectArrayElement(env, jarray, i);
+ *chunk = chunk_alloc((*env)->GetArrayLength(env, jbytearray));
+ (*env)->GetByteArrayRegion(env, jbytearray, 0, chunk->len, chunk->ptr);
+ (*env)->DeleteLocalRef(env, jbytearray);
+ }
+ return list;
+}
+
METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
private_charonservice_t *this)
{
@@ -206,7 +233,6 @@ METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
jmethodID method_id;
jobjectArray jcerts;
linked_list_t *list;
- jsize i;
androidjni_attach_thread(&env);
@@ -222,21 +248,39 @@ METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
{
goto failed;
}
- list = linked_list_create();
- for (i = 0; i < (*env)->GetArrayLength(env, jcerts); ++i)
- {
- chunk_t *ca_cert;
- jbyteArray jcert;
+ list = convert_array_of_byte_arrays(env, jcerts);
+ androidjni_detach_thread();
+ return list;
- ca_cert = malloc_thing(chunk_t);
- list->insert_last(list, ca_cert);
+failed:
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ return NULL;
+}
+
+METHOD(charonservice_t, get_user_certificate, linked_list_t*,
+ private_charonservice_t *this)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+ jobjectArray jencodings;
+ linked_list_t *list;
+
+ androidjni_attach_thread(&env);
- 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);
+ method_id = (*env)->GetMethodID(env,
+ android_charonvpnservice_class,
+ "getUserCertificate", "()[[B");
+ if (!method_id)
+ {
+ goto failed;
}
- (*env)->DeleteLocalRef(env, jcerts);
+ jencodings = (*env)->CallObjectMethod(env, this->vpn_service, method_id, NULL);
+ if (!jencodings)
+ {
+ goto failed;
+ }
+ list = convert_array_of_byte_arrays(env, jencodings);
androidjni_detach_thread();
return list;
@@ -260,17 +304,15 @@ METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
* @param username username (gets owned)
* @param password password (gets owned)
*/
-static void initiate(char *local, char *gateway, char *username, char *password)
+static void initiate(char *type, 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);
+ this->service = android_service_create(this->creds, type, local, gateway,
+ username, password);
}
/**
@@ -321,6 +363,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
.update_status = _update_status,
.bypass_socket = _bypass_socket,
.get_trusted_certificates = _get_trusted_certificates,
+ .get_user_certificate = _get_user_certificate,
.get_vpnservice_builder = _get_vpnservice_builder,
},
.attr = android_attr_create(),
@@ -477,15 +520,16 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void)
* Initiate SA
*/
JNI_METHOD(CharonVpnService, initiate, void,
- jstring jlocal_address, jstring jgateway, jstring jusername,
+ jstring jtype, jstring jlocal_address, jstring jgateway, jstring jusername,
jstring jpassword)
{
- char *local_address, *gateway, *username, *password;
+ char *type, *local_address, *gateway, *username, *password;
+ type = androidjni_convert_jstring(env, jtype);
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);
+ initiate(type, local_address, gateway, username, password);
}
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h
index 706eaa220..507010bad 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.h
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.h
@@ -86,6 +86,17 @@ struct charonservice_t {
linked_list_t *(*get_trusted_certificates)(charonservice_t *this);
/**
+ * Get the configured user certificate chain and private key via JNI
+ *
+ * The first item in the returned list is the private key, followed by the
+ * user certificate and any remaining elements of the certificate chain.
+ *
+ * @return list of DER encoded objects (as chunk_t*),
+ * NULL on failure
+ */
+ linked_list_t *(*get_user_certificate)(charonservice_t *this);
+
+ /**
* Get the current vpnservice_builder_t object
*
* @return VpnService.Builder instance