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.
GetCertificateByteArray(JNIEnv * env,const std::vector<std::string> cert_chain)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
VerifyX509CertChain(const std::vector<std::string> & cert_chain,const std::string & hostname,const std::string & auth_type)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
InitWithApplicationContext(JNIEnv * env,jobject context)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
AndroidNetworkLibraryImpl(JNIEnv * env)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
~AndroidNetworkLibraryImpl()144 AndroidNetworkLibraryImpl::~AndroidNetworkLibraryImpl() {
145 if (cert_verifier_class_)
146 jni::GetJNIEnv()->DeleteGlobalRef(cert_verifier_class_);
147 }
148
149