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
10using namespace android;
11
12namespace {
13
14const char* const kClassPathName = "android/net/http/CertificateChainValidator";
15
16// Convert X509 chain to DER format bytes.
17jobjectArray 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
51AndroidNetworkLibraryImpl::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
124void 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
133AndroidNetworkLibraryImpl::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
144AndroidNetworkLibraryImpl::~AndroidNetworkLibraryImpl() {
145  if (cert_verifier_class_)
146    jni::GetJNIEnv()->DeleteGlobalRef(cert_verifier_class_);
147}
148
149