Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mobile/library/common/jni/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ cc_library(
"jni_utility.h",
],
deps = [
":jni_helper_lib",
":jni_support_lib",
"//library/common/jni/import:jni_import_lib",
"//library/common/jni/types:jni_env_lib",
Expand Down
19 changes: 10 additions & 9 deletions mobile/library/common/jni/android_jni_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
bool is_cleartext_permitted(absl::string_view hostname) {
#if defined(__ANDROID_API__)
envoy_data host = Envoy::Data::Utility::copyToBridgeData(hostname);
JNIEnv* env = Envoy::JNI::get_env();
jstring java_host = Envoy::JNI::native_data_to_string(env, host);
Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have JniHelper call Envoy::JNI::get_env() in the constructor to simplify usage?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the use of Envoy::JNI::get_env() is not consistent. Some will use it and some won't. I believe Envoy::JNI::get_env() is also very Android specific, which means it won't work when running on HotSpot VM (non Android). In the future PR, I plan on fixing this as well, which could possibly be using Envoy::JNI::get_env() everywhere (or removing it).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. How confusing! Thanks for working on this.

jstring java_host = Envoy::JNI::native_data_to_string(jni_helper, host);
jclass jcls_AndroidNetworkLibrary =
Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary");
jmethodID jmid_isCleartextTrafficPermitted = env->GetStaticMethodID(
jmethodID jmid_isCleartextTrafficPermitted = jni_helper.getEnv()->GetStaticMethodID(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your plan is to (incrementally) replace code like this with jni_helper.GetStaticMethodID(...), right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. That's the plan. I will try to make small changes at a time, so it's easier to debug in case something breaks. Once everything has been fully migrated, I'll start removing JniHelper::getEnv().

jcls_AndroidNetworkLibrary, "isCleartextTrafficPermitted", "(Ljava/lang/String;)Z");
jboolean result = env->CallStaticBooleanMethod(jcls_AndroidNetworkLibrary,
jmid_isCleartextTrafficPermitted, java_host);
env->DeleteLocalRef(java_host);
jboolean result = jni_helper.getEnv()->CallStaticBooleanMethod(
jcls_AndroidNetworkLibrary, jmid_isCleartextTrafficPermitted, java_host);
jni_helper.getEnv()->DeleteLocalRef(java_host);
release_envoy_data(host);
return result == JNI_TRUE;
#else
Expand All @@ -36,12 +36,13 @@ bool is_cleartext_permitted(absl::string_view hostname) {

void tag_socket(int ifd, int uid, int tag) {
#if defined(__ANDROID_API__)
JNIEnv* env = Envoy::JNI::get_env();
Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env());
jclass jcls_AndroidNetworkLibrary =
Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary");
jmethodID jmid_tagSocket =
env->GetStaticMethodID(jcls_AndroidNetworkLibrary, "tagSocket", "(III)V");
env->CallStaticVoidMethod(jcls_AndroidNetworkLibrary, jmid_tagSocket, ifd, uid, tag);
jni_helper.getEnv()->GetStaticMethodID(jcls_AndroidNetworkLibrary, "tagSocket", "(III)V");
jni_helper.getEnv()->CallStaticVoidMethod(jcls_AndroidNetworkLibrary, jmid_tagSocket, ifd, uid,
tag);
#else
UNREFERENCED_PARAMETER(ifd);
UNREFERENCED_PARAMETER(uid);
Expand Down
78 changes: 42 additions & 36 deletions mobile/library/common/jni/android_network_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,81 +12,88 @@
// Helper functions call into AndroidNetworkLibrary, but they are not platform dependent
// because AndroidNetworkLibray can be called in non-Android platform with mock interfaces.

bool jvm_cert_is_issued_by_known_root(JNIEnv* env, jobject result) {
bool jvm_cert_is_issued_by_known_root(Envoy::JNI::JniHelper& jni_helper, jobject result) {
jclass jcls_AndroidCertVerifyResult =
Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidCertVerifyResult");
jmethodID jmid_isIssuedByKnownRoot =
env->GetMethodID(jcls_AndroidCertVerifyResult, "isIssuedByKnownRoot", "()Z");
jni_helper.getEnv()->GetMethodID(jcls_AndroidCertVerifyResult, "isIssuedByKnownRoot", "()Z");
Envoy::JNI::Exception::checkAndClear("jvm_cert_is_issued_by_known_root:GetMethodID");
ASSERT(jmid_isIssuedByKnownRoot);
bool is_issued_by_known_root = env->CallBooleanMethod(result, jmid_isIssuedByKnownRoot);
bool is_issued_by_known_root =
jni_helper.getEnv()->CallBooleanMethod(result, jmid_isIssuedByKnownRoot);
Envoy::JNI::Exception::checkAndClear("jvm_cert_is_issued_by_known_root:CallBooleanMethod");
env->DeleteLocalRef(jcls_AndroidCertVerifyResult);
jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidCertVerifyResult);
return is_issued_by_known_root;
}

envoy_cert_verify_status_t jvm_cert_get_status(JNIEnv* env, jobject j_result) {
envoy_cert_verify_status_t jvm_cert_get_status(Envoy::JNI::JniHelper& jni_helper,
jobject j_result) {
jclass jcls_AndroidCertVerifyResult =
Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidCertVerifyResult");
jmethodID jmid_getStatus = env->GetMethodID(jcls_AndroidCertVerifyResult, "getStatus", "()I");
jmethodID jmid_getStatus =
jni_helper.getEnv()->GetMethodID(jcls_AndroidCertVerifyResult, "getStatus", "()I");
Envoy::JNI::Exception::checkAndClear("jvm_cert_get_status:GetMethodID");
ASSERT(jmid_getStatus);
envoy_cert_verify_status_t result = CERT_VERIFY_STATUS_FAILED;
result = static_cast<envoy_cert_verify_status_t>(env->CallIntMethod(j_result, jmid_getStatus));
result = static_cast<envoy_cert_verify_status_t>(
jni_helper.getEnv()->CallIntMethod(j_result, jmid_getStatus));
Envoy::JNI::Exception::checkAndClear("jvm_cert_get_status:CallIntMethod");

env->DeleteLocalRef(jcls_AndroidCertVerifyResult);
jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidCertVerifyResult);
return result;
}

jobjectArray jvm_cert_get_certificate_chain_encoded(JNIEnv* env, jobject result) {
jobjectArray jvm_cert_get_certificate_chain_encoded(Envoy::JNI::JniHelper& jni_helper,
jobject result) {
jclass jcls_AndroidCertVerifyResult =
Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidCertVerifyResult");
jmethodID jmid_getCertificateChainEncoded =
env->GetMethodID(jcls_AndroidCertVerifyResult, "getCertificateChainEncoded", "()[[B");
jmethodID jmid_getCertificateChainEncoded = jni_helper.getEnv()->GetMethodID(
jcls_AndroidCertVerifyResult, "getCertificateChainEncoded", "()[[B");
Envoy::JNI::Exception::checkAndClear("jvm_cert_get_certificate_chain_encoded:GetMethodID");
jobjectArray certificate_chain =
static_cast<jobjectArray>(env->CallObjectMethod(result, jmid_getCertificateChainEncoded));
jobjectArray certificate_chain = static_cast<jobjectArray>(
jni_helper.getEnv()->CallObjectMethod(result, jmid_getCertificateChainEncoded));
Envoy::JNI::Exception::checkAndClear("jvm_cert_get_certificate_chain_encoded:CallObjectMethod");
env->DeleteLocalRef(jcls_AndroidCertVerifyResult);
jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidCertVerifyResult);
return certificate_chain;
}

static void ExtractCertVerifyResult(JNIEnv* env, jobject result, envoy_cert_verify_status_t* status,
static void ExtractCertVerifyResult(Envoy::JNI::JniHelper& jni_helper, jobject result,
envoy_cert_verify_status_t* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain) {
*status = jvm_cert_get_status(env, result);
*status = jvm_cert_get_status(jni_helper, result);
if (*status == CERT_VERIFY_STATUS_OK) {
*is_issued_by_known_root = jvm_cert_is_issued_by_known_root(env, result);
jobjectArray chain_byte_array = jvm_cert_get_certificate_chain_encoded(env, result);
*is_issued_by_known_root = jvm_cert_is_issued_by_known_root(jni_helper, result);
jobjectArray chain_byte_array = jvm_cert_get_certificate_chain_encoded(jni_helper, result);
if (chain_byte_array != nullptr) {
Envoy::JNI::JavaArrayOfByteArrayToStringVector(env, chain_byte_array, verified_chain);
Envoy::JNI::JavaArrayOfByteArrayToStringVector(jni_helper, chain_byte_array, verified_chain);
}
}
}

// `auth_type` and `host` are expected to be UTF-8 encoded.
jobject call_jvm_verify_x509_cert_chain(JNIEnv* env, const std::vector<std::string>& cert_chain,
jobject call_jvm_verify_x509_cert_chain(Envoy::JNI::JniHelper& jni_helper,
const std::vector<std::string>& cert_chain,
std::string auth_type, absl::string_view hostname) {
jni_log("[Envoy]", "jvm_verify_x509_cert_chain");
jclass jcls_AndroidNetworkLibrary =
Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary");
jmethodID jmid_verifyServerCertificates = env->GetStaticMethodID(
jmethodID jmid_verifyServerCertificates = jni_helper.getEnv()->GetStaticMethodID(
jcls_AndroidNetworkLibrary, "verifyServerCertificates",
"([[B[B[B)Lio/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult;");
Envoy::JNI::Exception::checkAndClear("call_jvm_verify_x509_cert_chain:GetStaticMethodID");
jobjectArray chain_byte_array = Envoy::JNI::ToJavaArrayOfByteArray(env, cert_chain);
jbyteArray auth_string = Envoy::JNI::ToJavaByteArray(env, auth_type);
jobjectArray chain_byte_array = Envoy::JNI::ToJavaArrayOfByteArray(jni_helper, cert_chain);
jbyteArray auth_string = Envoy::JNI::ToJavaByteArray(jni_helper, auth_type);
jbyteArray host_string = Envoy::JNI::ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(hostname.data()), hostname.length());
jobject result =
env->CallStaticObjectMethod(jcls_AndroidNetworkLibrary, jmid_verifyServerCertificates,
chain_byte_array, auth_string, host_string);
jni_helper, reinterpret_cast<const uint8_t*>(hostname.data()), hostname.length());
jobject result = jni_helper.getEnv()->CallStaticObjectMethod(
jcls_AndroidNetworkLibrary, jmid_verifyServerCertificates, chain_byte_array, auth_string,
host_string);
Envoy::JNI::Exception::checkAndClear("call_jvm_verify_x509_cert_chain:CallStaticObjectMethod");
env->DeleteLocalRef(chain_byte_array);
env->DeleteLocalRef(auth_string);
env->DeleteLocalRef(host_string);
env->DeleteLocalRef(jcls_AndroidNetworkLibrary);
jni_helper.getEnv()->DeleteLocalRef(chain_byte_array);
jni_helper.getEnv()->DeleteLocalRef(auth_string);
jni_helper.getEnv()->DeleteLocalRef(host_string);
jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidNetworkLibrary);
return result;
}

Expand All @@ -96,18 +103,17 @@ static void jvm_verify_x509_cert_chain(const std::vector<std::string>& cert_chai
envoy_cert_verify_status_t* status,
bool* is_issued_by_known_root,
std::vector<std::string>* verified_chain) {
JNIEnv* env = Envoy::JNI::get_env();
jobject result = call_jvm_verify_x509_cert_chain(env, cert_chain, auth_type, hostname);
Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env());
jobject result = call_jvm_verify_x509_cert_chain(jni_helper, cert_chain, auth_type, hostname);
if (Envoy::JNI::Exception::checkAndClear()) {
*status = CERT_VERIFY_STATUS_NOT_YET_VALID;
} else {
ExtractCertVerifyResult(Envoy::JNI::get_env(), result, status, is_issued_by_known_root,
verified_chain);
ExtractCertVerifyResult(jni_helper, result, status, is_issued_by_known_root, verified_chain);
if (Envoy::JNI::Exception::checkAndClear()) {
*status = CERT_VERIFY_STATUS_FAILED;
}
}
env->DeleteLocalRef(result);
jni_helper.getEnv()->DeleteLocalRef(result);
}

envoy_cert_validation_result verify_x509_cert_chain(const std::vector<std::string>& certs,
Expand Down
4 changes: 3 additions & 1 deletion mobile/library/common/jni/android_network_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
#include "library/common/api/c_types.h"
#include "library/common/extensions/cert_validator/platform_bridge/c_types.h"
#include "library/common/jni/import/jni_import.h"
#include "library/common/jni/jni_helper.h"

// NOLINT(namespace-envoy)

/* Calls up through JNI to validate given certificates.
*/
jobject call_jvm_verify_x509_cert_chain(JNIEnv* env, const std::vector<std::string>& cert_chain,
jobject call_jvm_verify_x509_cert_chain(Envoy::JNI::JniHelper& jni_helper,
const std::vector<std::string>& cert_chain,
std::string auth_type, absl::string_view hostname);

envoy_cert_validation_result verify_x509_cert_chain(const std::vector<std::string>& certs,
Expand Down
Loading