• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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