diff options
Diffstat (limited to 'src/frontends/android/jni/libandroidbridge')
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**)¤t) == 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 |