blob: 433ca611e9fc71fefda0e6908e4ac0b2a28eae68 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android/net/android_network_library_impl.h"
#include "base/logging.h"
#include "android/jni/jni_utils.h"
using namespace android;
namespace {
const char* const kClassPathName = "android/net/http/CertificateChainValidator";
// Convert X509 chain to DER format bytes.
jobjectArray GetCertificateByteArray(
JNIEnv* env,
const std::vector<std::string> cert_chain) {
size_t count = cert_chain.size();
DCHECK_GT(count, 0U);
// TODO(joth): See if we can centrally cache common classes like this, e.g.
// as JniConstants does.
jclass byte_array_class = env->FindClass("[B");
jobjectArray joa = env->NewObjectArray(count, byte_array_class, NULL);
if (joa == NULL)
return NULL;
for (size_t i = 0; i < count; ++i) {
size_t len = cert_chain[i].length();
jbyteArray byte_array = env->NewByteArray(len);
if (!byte_array) {
env->DeleteLocalRef(joa);
return NULL;
}
jbyte* bytes = env->GetByteArrayElements(byte_array, NULL);
DCHECK(bytes);
size_t copied = cert_chain[i].copy(reinterpret_cast<char*>(bytes), len);
DCHECK_EQ(copied, len);
env->ReleaseByteArrayElements(byte_array, bytes, 0);
env->SetObjectArrayElement(joa, i, byte_array);
env->DeleteLocalRef(byte_array);
}
return joa;
}
} // namespace
AndroidNetworkLibraryImpl::VerifyResult
AndroidNetworkLibraryImpl::VerifyX509CertChain(
const std::vector<std::string>& cert_chain,
const std::string& hostname,
const std::string& auth_type) {
if (!cert_verifier_class_)
return VERIFY_INVOCATION_ERROR;
JNIEnv* env = jni::GetJNIEnv();
DCHECK(env);
static jmethodID verify_fn = env->GetStaticMethodID(
cert_verifier_class_, "verifyServerCertificates",
"([[BLjava/lang/String;Ljava/lang/String;)Landroid/net/http/SslError;");
if (jni::CheckException(env)) {
LOG(ERROR) << "verifyServerCertificates method not found; skipping";
return VERIFY_INVOCATION_ERROR;
}
DCHECK(verify_fn);
jobjectArray chain_byte_array = GetCertificateByteArray(env, cert_chain);
if (!chain_byte_array)
return VERIFY_INVOCATION_ERROR;
jstring host_string = jni::ConvertUTF8ToJavaString(env, hostname);
DCHECK(host_string);
jstring auth_string = jni::ConvertUTF8ToJavaString(env, auth_type);
DCHECK(auth_string);
jobject error = env->CallStaticObjectMethod(cert_verifier_class_, verify_fn,
chain_byte_array, host_string,
auth_string);
env->DeleteLocalRef(chain_byte_array);
env->DeleteLocalRef(host_string);
env->DeleteLocalRef(auth_string);
VerifyResult result = VERIFY_INVOCATION_ERROR;
if (!jni::CheckException(env)) {
if (!error) {
result = VERIFY_OK;
} else {
jclass error_class = env->GetObjectClass(error);
DCHECK(error_class);
static jmethodID error_fn = env->GetMethodID(error_class,
"getPrimaryError", "()I");
if (error_fn) {
int code = env->CallIntMethod(error, error_fn);
if (!jni::CheckException(env)) {
if (code == 2) { // SSL_IDMISMATCH == 2
result = VERIFY_BAD_HOSTNAME;
} else if (code == 3) { // SSL_UNTRUSTED == 3
result = VERIFY_NO_TRUSTED_ROOT;
}
}
}
env->DeleteLocalRef(error);
}
} else {
// an uncaught exception has happened in java code, clear it and return
// a proper error
env->ExceptionClear();
result = VERIFY_INVOCATION_ERROR;
}
// TODO(joth): This balances the GetJNIEnv call; we need to detach as
// currently this method is called in chrome from a worker pool thread that
// may shutdown at anytime. However this assumption should not be baked in
// here: another user of the function may not want to have their thread
// detached at this point.
jni::DetachFromVM();
return result;
}
// static
void AndroidNetworkLibraryImpl::InitWithApplicationContext(JNIEnv* env,
jobject context) {
// Currently ignoring |context| as it is not needed (but remains in signature
// for API consistency with the equivalent method on class AndroidOS).
if (!net::AndroidNetworkLibrary::GetSharedInstance())
net::AndroidNetworkLibrary::RegisterSharedInstance(
new AndroidNetworkLibraryImpl(env));
}
AndroidNetworkLibraryImpl::AndroidNetworkLibraryImpl(JNIEnv* env)
: cert_verifier_class_(NULL) {
jclass cls = env->FindClass(kClassPathName);
if (jni::CheckException(env) || !cls) {
NOTREACHED() << "Unable to load class " << kClassPathName;
} else {
cert_verifier_class_ = static_cast<jclass>(env->NewGlobalRef(cls));
env->DeleteLocalRef(cls);
}
}
AndroidNetworkLibraryImpl::~AndroidNetworkLibraryImpl() {
if (cert_verifier_class_)
jni::GetJNIEnv()->DeleteGlobalRef(cert_verifier_class_);
}