1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "android/net/android_network_library_impl.h" 6 7 #include "base/logging.h" 8 #include "android/jni/jni_utils.h" 9 10 using namespace android; 11 12 namespace { 13 14 const char* const kClassPathName = "android/net/http/CertificateChainValidator"; 15 16 // Convert X509 chain to DER format bytes. 17 jobjectArray GetCertificateByteArray( 18 JNIEnv* env, 19 const std::vector<std::string> cert_chain) { 20 size_t count = cert_chain.size(); 21 DCHECK_GT(count, 0U); 22 // TODO(joth): See if we can centrally cache common classes like this, e.g. 23 // as JniConstants does. 24 jclass byte_array_class = env->FindClass("[B"); 25 jobjectArray joa = env->NewObjectArray(count, byte_array_class, NULL); 26 if (joa == NULL) 27 return NULL; 28 29 for (size_t i = 0; i < count; ++i) { 30 size_t len = cert_chain[i].length(); 31 32 jbyteArray byte_array = env->NewByteArray(len); 33 if (!byte_array) { 34 env->DeleteLocalRef(joa); 35 return NULL; 36 } 37 38 jbyte* bytes = env->GetByteArrayElements(byte_array, NULL); 39 DCHECK(bytes); 40 size_t copied = cert_chain[i].copy(reinterpret_cast<char*>(bytes), len); 41 DCHECK_EQ(copied, len); 42 env->ReleaseByteArrayElements(byte_array, bytes, 0); 43 env->SetObjectArrayElement(joa, i, byte_array); 44 env->DeleteLocalRef(byte_array); 45 } 46 return joa; 47 } 48 49 } // namespace 50 51 AndroidNetworkLibraryImpl::VerifyResult 52 AndroidNetworkLibraryImpl::VerifyX509CertChain( 53 const std::vector<std::string>& cert_chain, 54 const std::string& hostname, 55 const std::string& auth_type) { 56 if (!cert_verifier_class_) 57 return VERIFY_INVOCATION_ERROR; 58 59 JNIEnv* env = jni::GetJNIEnv(); 60 DCHECK(env); 61 62 static jmethodID verify_fn = env->GetStaticMethodID( 63 cert_verifier_class_, "verifyServerCertificates", 64 "([[BLjava/lang/String;Ljava/lang/String;)Landroid/net/http/SslError;"); 65 if (jni::CheckException(env)) { 66 LOG(ERROR) << "verifyServerCertificates method not found; skipping"; 67 return VERIFY_INVOCATION_ERROR; 68 } 69 DCHECK(verify_fn); 70 71 jobjectArray chain_byte_array = GetCertificateByteArray(env, cert_chain); 72 if (!chain_byte_array) 73 return VERIFY_INVOCATION_ERROR; 74 75 jstring host_string = jni::ConvertUTF8ToJavaString(env, hostname); 76 DCHECK(host_string); 77 jstring auth_string = jni::ConvertUTF8ToJavaString(env, auth_type); 78 DCHECK(auth_string); 79 80 jobject error = env->CallStaticObjectMethod(cert_verifier_class_, verify_fn, 81 chain_byte_array, host_string, 82 auth_string); 83 env->DeleteLocalRef(chain_byte_array); 84 env->DeleteLocalRef(host_string); 85 env->DeleteLocalRef(auth_string); 86 87 VerifyResult result = VERIFY_INVOCATION_ERROR; 88 if (!jni::CheckException(env)) { 89 if (!error) { 90 result = VERIFY_OK; 91 } else { 92 jclass error_class = env->GetObjectClass(error); 93 DCHECK(error_class); 94 static jmethodID error_fn = env->GetMethodID(error_class, 95 "getPrimaryError", "()I"); 96 if (error_fn) { 97 int code = env->CallIntMethod(error, error_fn); 98 if (!jni::CheckException(env)) { 99 if (code == 2) { // SSL_IDMISMATCH == 2 100 result = VERIFY_BAD_HOSTNAME; 101 } else if (code == 3) { // SSL_UNTRUSTED == 3 102 result = VERIFY_NO_TRUSTED_ROOT; 103 } 104 } 105 } 106 env->DeleteLocalRef(error); 107 } 108 } else { 109 // an uncaught exception has happened in java code, clear it and return 110 // a proper error 111 env->ExceptionClear(); 112 result = VERIFY_INVOCATION_ERROR; 113 } 114 // TODO(joth): This balances the GetJNIEnv call; we need to detach as 115 // currently this method is called in chrome from a worker pool thread that 116 // may shutdown at anytime. However this assumption should not be baked in 117 // here: another user of the function may not want to have their thread 118 // detached at this point. 119 jni::DetachFromVM(); 120 return result; 121 } 122 123 // static 124 void AndroidNetworkLibraryImpl::InitWithApplicationContext(JNIEnv* env, 125 jobject context) { 126 // Currently ignoring |context| as it is not needed (but remains in signature 127 // for API consistency with the equivalent method on class AndroidOS). 128 if (!net::AndroidNetworkLibrary::GetSharedInstance()) 129 net::AndroidNetworkLibrary::RegisterSharedInstance( 130 new AndroidNetworkLibraryImpl(env)); 131 } 132 133 AndroidNetworkLibraryImpl::AndroidNetworkLibraryImpl(JNIEnv* env) 134 : cert_verifier_class_(NULL) { 135 jclass cls = env->FindClass(kClassPathName); 136 if (jni::CheckException(env) || !cls) { 137 NOTREACHED() << "Unable to load class " << kClassPathName; 138 } else { 139 cert_verifier_class_ = static_cast<jclass>(env->NewGlobalRef(cls)); 140 env->DeleteLocalRef(cls); 141 } 142 } 143 144 AndroidNetworkLibraryImpl::~AndroidNetworkLibraryImpl() { 145 if (cert_verifier_class_) 146 jni::GetJNIEnv()->DeleteGlobalRef(cert_verifier_class_); 147 } 148 149