• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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_webview/native/aw_contents_client_bridge.h"
6 
7 #include "android_webview/common/devtools_instrumentation.h"
8 #include "android_webview/native/aw_contents.h"
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_array.h"
11 #include "base/android/jni_string.h"
12 #include "base/callback_helpers.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "jni/AwContentsClientBridge_jni.h"
18 #include "net/android/keystore_openssl.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/ssl/openssl_client_key_store.h"
21 #include "net/ssl/ssl_cert_request_info.h"
22 #include "net/ssl/ssl_client_cert_type.h"
23 #include "url/gurl.h"
24 
25 using base::android::AttachCurrentThread;
26 using base::android::ConvertJavaStringToUTF16;
27 using base::android::ConvertUTF8ToJavaString;
28 using base::android::ConvertUTF16ToJavaString;
29 using base::android::JavaRef;
30 using base::android::ScopedJavaLocalRef;
31 using content::BrowserThread;
32 
33 namespace android_webview {
34 
35 typedef net::OpenSSLClientKeyStore::ScopedEVP_PKEY ScopedEVP_PKEY;
36 
37 namespace {
38 
39 // Must be called on the I/O thread to record a client certificate
40 // and its private key in the OpenSSLClientKeyStore.
RecordClientCertificateKey(const scoped_refptr<net::X509Certificate> & client_cert,ScopedEVP_PKEY private_key)41 void RecordClientCertificateKey(
42     const scoped_refptr<net::X509Certificate>& client_cert,
43     ScopedEVP_PKEY private_key) {
44   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
45   net::OpenSSLClientKeyStore::GetInstance()->RecordClientCertPrivateKey(
46       client_cert.get(), private_key.get());
47 }
48 
49 }  // namespace
50 
AwContentsClientBridge(JNIEnv * env,jobject obj)51 AwContentsClientBridge::AwContentsClientBridge(JNIEnv* env, jobject obj)
52     : java_ref_(env, obj) {
53   DCHECK(obj);
54   Java_AwContentsClientBridge_setNativeContentsClientBridge(
55       env, obj, reinterpret_cast<intptr_t>(this));
56 }
57 
~AwContentsClientBridge()58 AwContentsClientBridge::~AwContentsClientBridge() {
59   JNIEnv* env = AttachCurrentThread();
60 
61   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
62   if (obj.is_null())
63     return;
64   // Clear the weak reference from the java peer to the native object since
65   // it is possible that java object lifetime can exceed the AwContens.
66   Java_AwContentsClientBridge_setNativeContentsClientBridge(env, obj.obj(), 0);
67 }
68 
AllowCertificateError(int cert_error,net::X509Certificate * cert,const GURL & request_url,const base::Callback<void (bool)> & callback,bool * cancel_request)69 void AwContentsClientBridge::AllowCertificateError(
70     int cert_error,
71     net::X509Certificate* cert,
72     const GURL& request_url,
73     const base::Callback<void(bool)>& callback,
74     bool* cancel_request) {
75 
76   DCHECK_CURRENTLY_ON(BrowserThread::UI);
77   JNIEnv* env = AttachCurrentThread();
78 
79   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
80   if (obj.is_null())
81     return;
82 
83   std::string der_string;
84   net::X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_string);
85   ScopedJavaLocalRef<jbyteArray> jcert = base::android::ToJavaByteArray(
86       env,
87       reinterpret_cast<const uint8*>(der_string.data()),
88       der_string.length());
89   ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(
90       env, request_url.spec()));
91   // We need to add the callback before making the call to java side,
92   // as it may do a synchronous callback prior to returning.
93   int request_id = pending_cert_error_callbacks_.Add(
94       new CertErrorCallback(callback));
95   *cancel_request = !Java_AwContentsClientBridge_allowCertificateError(
96       env, obj.obj(), cert_error, jcert.obj(), jurl.obj(), request_id);
97   // if the request is cancelled, then cancel the stored callback
98   if (*cancel_request) {
99     pending_cert_error_callbacks_.Remove(request_id);
100   }
101 }
102 
ProceedSslError(JNIEnv * env,jobject obj,jboolean proceed,jint id)103 void AwContentsClientBridge::ProceedSslError(JNIEnv* env, jobject obj,
104                                              jboolean proceed, jint id) {
105   DCHECK_CURRENTLY_ON(BrowserThread::UI);
106   CertErrorCallback* callback = pending_cert_error_callbacks_.Lookup(id);
107   if (!callback || callback->is_null()) {
108     LOG(WARNING) << "Ignoring unexpected ssl error proceed callback";
109     return;
110   }
111   callback->Run(proceed);
112   pending_cert_error_callbacks_.Remove(id);
113 }
114 
115 // This method is inspired by SelectClientCertificate() in
116 // chrome/browser/ui/android/ssl_client_certificate_request.cc
SelectClientCertificate(net::SSLCertRequestInfo * cert_request_info,const SelectCertificateCallback & callback)117 void AwContentsClientBridge::SelectClientCertificate(
118       net::SSLCertRequestInfo* cert_request_info,
119       const SelectCertificateCallback& callback) {
120   DCHECK_CURRENTLY_ON(BrowserThread::UI);
121 
122   // Add the callback to id map.
123   int request_id = pending_client_cert_request_callbacks_.Add(
124       new SelectCertificateCallback(callback));
125   // Make sure callback is run on error.
126   base::ScopedClosureRunner guard(base::Bind(
127       &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
128       base::Unretained(this),
129       request_id));
130 
131   JNIEnv* env = base::android::AttachCurrentThread();
132   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
133   if (obj.is_null())
134     return;
135 
136   // Build the |key_types| JNI parameter, as a String[]
137   std::vector<std::string> key_types;
138   for (size_t i = 0; i < cert_request_info->cert_key_types.size(); ++i) {
139     switch (cert_request_info->cert_key_types[i]) {
140       case net::CLIENT_CERT_RSA_SIGN:
141         key_types.push_back("RSA");
142         break;
143       case net::CLIENT_CERT_DSS_SIGN:
144         key_types.push_back("DSA");
145         break;
146       case net::CLIENT_CERT_ECDSA_SIGN:
147         key_types.push_back("ECDSA");
148         break;
149       default:
150         // Ignore unknown types.
151         break;
152     }
153   }
154 
155   ScopedJavaLocalRef<jobjectArray> key_types_ref =
156       base::android::ToJavaArrayOfStrings(env, key_types);
157   if (key_types_ref.is_null()) {
158     LOG(ERROR) << "Could not create key types array (String[])";
159     return;
160   }
161 
162   // Build the |encoded_principals| JNI parameter, as a byte[][]
163   ScopedJavaLocalRef<jobjectArray> principals_ref =
164       base::android::ToJavaArrayOfByteArray(
165           env, cert_request_info->cert_authorities);
166   if (principals_ref.is_null()) {
167     LOG(ERROR) << "Could not create principals array (byte[][])";
168     return;
169   }
170 
171   // Build the |host_name| and |port| JNI parameters, as a String and
172   // a jint.
173   ScopedJavaLocalRef<jstring> host_name_ref =
174       base::android::ConvertUTF8ToJavaString(
175           env, cert_request_info->host_and_port.host());
176 
177   Java_AwContentsClientBridge_selectClientCertificate(
178       env,
179       obj.obj(),
180       request_id,
181       key_types_ref.obj(),
182       principals_ref.obj(),
183       host_name_ref.obj(),
184       cert_request_info->host_and_port.port());
185 
186   // Release the guard.
187   ignore_result(guard.Release());
188 }
189 
190 // This method is inspired by OnSystemRequestCompletion() in
191 // chrome/browser/ui/android/ssl_client_certificate_request.cc
ProvideClientCertificateResponse(JNIEnv * env,jobject obj,int request_id,jobjectArray encoded_chain_ref,jobject private_key_ref)192 void AwContentsClientBridge::ProvideClientCertificateResponse(
193     JNIEnv* env,
194     jobject obj,
195     int request_id,
196     jobjectArray encoded_chain_ref,
197     jobject private_key_ref) {
198   DCHECK_CURRENTLY_ON(BrowserThread::UI);
199 
200   SelectCertificateCallback* callback =
201       pending_client_cert_request_callbacks_.Lookup(request_id);
202   DCHECK(callback);
203 
204   // Make sure callback is run on error.
205   base::ScopedClosureRunner guard(base::Bind(
206       &AwContentsClientBridge::HandleErrorInClientCertificateResponse,
207       base::Unretained(this),
208       request_id));
209   if (encoded_chain_ref == NULL || private_key_ref == NULL) {
210     LOG(ERROR) << "Client certificate request cancelled";
211     return;
212   }
213   // Convert the encoded chain to a vector of strings.
214   std::vector<std::string> encoded_chain_strings;
215   if (encoded_chain_ref) {
216     base::android::JavaArrayOfByteArrayToStringVector(
217         env, encoded_chain_ref, &encoded_chain_strings);
218   }
219 
220   std::vector<base::StringPiece> encoded_chain;
221   for (size_t i = 0; i < encoded_chain_strings.size(); ++i)
222     encoded_chain.push_back(encoded_chain_strings[i]);
223 
224   // Create the X509Certificate object from the encoded chain.
225   scoped_refptr<net::X509Certificate> client_cert(
226       net::X509Certificate::CreateFromDERCertChain(encoded_chain));
227   if (!client_cert.get()) {
228     LOG(ERROR) << "Could not decode client certificate chain";
229     return;
230   }
231 
232   // Create an EVP_PKEY wrapper for the private key JNI reference.
233   ScopedEVP_PKEY private_key(
234       net::android::GetOpenSSLPrivateKeyWrapper(private_key_ref));
235   if (!private_key.get()) {
236     LOG(ERROR) << "Could not create OpenSSL wrapper for private key";
237     return;
238   }
239 
240   // RecordClientCertificateKey() must be called on the I/O thread,
241   // before the callback is called with the selected certificate on
242   // the UI thread.
243   content::BrowserThread::PostTaskAndReply(
244       content::BrowserThread::IO,
245       FROM_HERE,
246       base::Bind(&RecordClientCertificateKey,
247                  client_cert,
248                  base::Passed(&private_key)),
249       base::Bind(*callback, client_cert));
250   pending_client_cert_request_callbacks_.Remove(request_id);
251 
252   // Release the guard.
253   ignore_result(guard.Release());
254 }
255 
RunJavaScriptDialog(content::JavaScriptMessageType message_type,const GURL & origin_url,const base::string16 & message_text,const base::string16 & default_prompt_text,const content::JavaScriptDialogManager::DialogClosedCallback & callback)256 void AwContentsClientBridge::RunJavaScriptDialog(
257     content::JavaScriptMessageType message_type,
258     const GURL& origin_url,
259     const base::string16& message_text,
260     const base::string16& default_prompt_text,
261     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
262   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
263   JNIEnv* env = AttachCurrentThread();
264 
265   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
266   if (obj.is_null()) {
267     callback.Run(false, base::string16());
268     return;
269   }
270 
271   int callback_id = pending_js_dialog_callbacks_.Add(
272       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
273   ScopedJavaLocalRef<jstring> jurl(
274       ConvertUTF8ToJavaString(env, origin_url.spec()));
275   ScopedJavaLocalRef<jstring> jmessage(
276       ConvertUTF16ToJavaString(env, message_text));
277 
278   switch (message_type) {
279     case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: {
280       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsAlert");
281       Java_AwContentsClientBridge_handleJsAlert(
282           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
283       break;
284     }
285     case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: {
286       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsConfirm");
287       Java_AwContentsClientBridge_handleJsConfirm(
288           env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
289       break;
290     }
291     case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: {
292       ScopedJavaLocalRef<jstring> jdefault_value(
293           ConvertUTF16ToJavaString(env, default_prompt_text));
294       devtools_instrumentation::ScopedEmbedderCallbackTask("onJsPrompt");
295       Java_AwContentsClientBridge_handleJsPrompt(env,
296                                                  obj.obj(),
297                                                  jurl.obj(),
298                                                  jmessage.obj(),
299                                                  jdefault_value.obj(),
300                                                  callback_id);
301       break;
302     }
303     default:
304        NOTREACHED();
305   }
306 }
307 
RunBeforeUnloadDialog(const GURL & origin_url,const base::string16 & message_text,const content::JavaScriptDialogManager::DialogClosedCallback & callback)308 void AwContentsClientBridge::RunBeforeUnloadDialog(
309     const GURL& origin_url,
310     const base::string16& message_text,
311     const content::JavaScriptDialogManager::DialogClosedCallback& callback) {
312   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313   JNIEnv* env = AttachCurrentThread();
314 
315   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
316   if (obj.is_null()) {
317     callback.Run(false, base::string16());
318     return;
319   }
320 
321   int callback_id = pending_js_dialog_callbacks_.Add(
322       new content::JavaScriptDialogManager::DialogClosedCallback(callback));
323   ScopedJavaLocalRef<jstring> jurl(
324       ConvertUTF8ToJavaString(env, origin_url.spec()));
325   ScopedJavaLocalRef<jstring> jmessage(
326       ConvertUTF16ToJavaString(env, message_text));
327 
328   devtools_instrumentation::ScopedEmbedderCallbackTask("onJsBeforeUnload");
329   Java_AwContentsClientBridge_handleJsBeforeUnload(
330       env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id);
331 }
332 
ShouldOverrideUrlLoading(const base::string16 & url)333 bool AwContentsClientBridge::ShouldOverrideUrlLoading(
334     const base::string16& url) {
335   JNIEnv* env = AttachCurrentThread();
336   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
337   if (obj.is_null())
338     return false;
339   ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url);
340   devtools_instrumentation::ScopedEmbedderCallbackTask(
341       "shouldOverrideUrlLoading");
342   return Java_AwContentsClientBridge_shouldOverrideUrlLoading(
343       env, obj.obj(),
344       jurl.obj());
345 }
346 
ConfirmJsResult(JNIEnv * env,jobject,int id,jstring prompt)347 void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env,
348                                              jobject,
349                                              int id,
350                                              jstring prompt) {
351   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352   content::JavaScriptDialogManager::DialogClosedCallback* callback =
353       pending_js_dialog_callbacks_.Lookup(id);
354   if (!callback) {
355     LOG(WARNING) << "Unexpected JS dialog confirm. " << id;
356     return;
357   }
358   base::string16 prompt_text;
359   if (prompt) {
360     prompt_text = ConvertJavaStringToUTF16(env, prompt);
361   }
362   callback->Run(true, prompt_text);
363   pending_js_dialog_callbacks_.Remove(id);
364 }
365 
CancelJsResult(JNIEnv *,jobject,int id)366 void AwContentsClientBridge::CancelJsResult(JNIEnv*, jobject, int id) {
367   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
368   content::JavaScriptDialogManager::DialogClosedCallback* callback =
369       pending_js_dialog_callbacks_.Lookup(id);
370   if (!callback) {
371     LOG(WARNING) << "Unexpected JS dialog cancel. " << id;
372     return;
373   }
374   callback->Run(false, base::string16());
375   pending_js_dialog_callbacks_.Remove(id);
376 }
377 
378 // Use to cleanup if there is an error in client certificate response.
HandleErrorInClientCertificateResponse(int request_id)379 void AwContentsClientBridge::HandleErrorInClientCertificateResponse(
380     int request_id) {
381   SelectCertificateCallback* callback =
382       pending_client_cert_request_callbacks_.Lookup(request_id);
383   callback->Run(scoped_refptr<net::X509Certificate>());
384   pending_client_cert_request_callbacks_.Remove(request_id);
385 }
386 
RegisterAwContentsClientBridge(JNIEnv * env)387 bool RegisterAwContentsClientBridge(JNIEnv* env) {
388   return RegisterNativesImpl(env);
389 }
390 
391 }  // namespace android_webview
392