Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(277)

Side by Side Diff: chrome/browser/ui/login/login_handler.cc

Issue 2067933002: Use correct origin when prompting for proxy authentication. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/login/login_handler.h" 5 #include "chrome/browser/ui/login/login_handler.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 14 matching lines...) Expand all
25 #include "components/url_formatter/elide_url.h" 25 #include "components/url_formatter/elide_url.h"
26 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_registrar.h" 27 #include "content/public/browser/notification_registrar.h"
28 #include "content/public/browser/notification_service.h" 28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/render_frame_host.h" 29 #include "content/public/browser/render_frame_host.h"
30 #include "content/public/browser/resource_dispatcher_host.h" 30 #include "content/public/browser/resource_dispatcher_host.h"
31 #include "content/public/browser/resource_request_info.h" 31 #include "content/public/browser/resource_request_info.h"
32 #include "content/public/browser/web_contents.h" 32 #include "content/public/browser/web_contents.h"
33 #include "content/public/common/origin_util.h" 33 #include "content/public/common/origin_util.h"
34 #include "net/base/auth.h" 34 #include "net/base/auth.h"
35 #include "net/base/host_port_pair.h"
35 #include "net/base/load_flags.h" 36 #include "net/base/load_flags.h"
36 #include "net/http/http_auth_scheme.h" 37 #include "net/http/http_auth_scheme.h"
37 #include "net/http/http_transaction_factory.h" 38 #include "net/http/http_transaction_factory.h"
38 #include "net/url_request/url_request.h" 39 #include "net/url_request/url_request.h"
39 #include "net/url_request/url_request_context.h" 40 #include "net/url_request/url_request_context.h"
40 #include "ui/base/l10n/l10n_util.h" 41 #include "ui/base/l10n/l10n_util.h"
41 #include "ui/gfx/text_elider.h" 42 #include "ui/gfx/text_elider.h"
42 43
43 #if defined(ENABLE_EXTENSIONS) 44 #if defined(ENABLE_EXTENSIONS)
44 #include "components/guest_view/browser/guest_view_base.h" 45 #include "components/guest_view/browser/guest_view_base.h"
(...skipping 13 matching lines...) Expand all
58 59
59 namespace { 60 namespace {
60 61
61 // Helper to remove the ref from an net::URLRequest to the LoginHandler. 62 // Helper to remove the ref from an net::URLRequest to the LoginHandler.
62 // Should only be called from the IO thread, since it accesses an 63 // Should only be called from the IO thread, since it accesses an
63 // net::URLRequest. 64 // net::URLRequest.
64 void ResetLoginHandlerForRequest(net::URLRequest* request) { 65 void ResetLoginHandlerForRequest(net::URLRequest* request) {
65 ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request); 66 ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request);
66 } 67 }
67 68
68 // Helper to create a PasswordForm for PasswordManager to start looking for
69 // saved credentials.
70 PasswordForm MakeInputForPasswordManager(const GURL& request_url,
71 net::AuthChallengeInfo* auth_info) {
72 PasswordForm dialog_form;
73 if (base::LowerCaseEqualsASCII(auth_info->scheme, net::kBasicAuthScheme)) {
74 dialog_form.scheme = PasswordForm::SCHEME_BASIC;
75 } else if (base::LowerCaseEqualsASCII(auth_info->scheme,
76 net::kDigestAuthScheme)) {
77 dialog_form.scheme = PasswordForm::SCHEME_DIGEST;
78 } else {
79 dialog_form.scheme = PasswordForm::SCHEME_OTHER;
80 }
81 std::string host_and_port(auth_info->challenger.ToString());
82 if (auth_info->is_proxy) {
83 std::string origin = host_and_port;
84 // We don't expect this to already start with http:// or https://.
85 DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0);
86 origin = std::string("http://") + origin;
asanka 2016/06/14 18:50:48 vabr: This logic doesn't distinguish between http
vabr (Chromium) 2016/06/15 08:50:06 Thanks for the heads-up. I agree with your argumen
87 dialog_form.origin = GURL(origin);
88 } else if (!auth_info->challenger.Equals(
89 net::HostPortPair::FromURL(request_url))) {
90 dialog_form.origin = GURL();
91 NOTREACHED(); // crbug.com/32718
92 } else {
93 dialog_form.origin = GURL(request_url.scheme() + "://" + host_and_port);
asanka 2016/06/14 18:50:48 vabr: Note that host_and_port will always contain
vabr (Chromium) 2016/06/15 08:50:06 Acknowledged.
94 }
95 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info);
96 return dialog_form;
97 }
98
99 void ShowLoginPrompt(const GURL& request_url,
100 net::AuthChallengeInfo* auth_info,
101 LoginHandler* handler) {
102 DCHECK_CURRENTLY_ON(BrowserThread::UI);
103 WebContents* parent_contents = handler->GetWebContentsForLogin();
104 if (!parent_contents)
105 return;
106 prerender::PrerenderContents* prerender_contents =
107 prerender::PrerenderContents::FromWebContents(parent_contents);
108 if (prerender_contents) {
109 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED);
110 return;
111 }
112
113 base::string16 authority = l10n_util::GetStringFUTF16(
114 auth_info->is_proxy ? IDS_LOGIN_DIALOG_PROXY_AUTHORITY
115 : IDS_LOGIN_DIALOG_AUTHORITY,
116 url_formatter::FormatUrlForSecurityDisplay(request_url));
117 base::string16 explanation;
118 if (!content::IsOriginSecure(request_url)) {
119 explanation =
120 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT);
121 }
122
123 password_manager::PasswordManager* password_manager =
124 handler->GetPasswordManagerForLogin();
125
126 if (!password_manager) {
127 #if defined(ENABLE_EXTENSIONS)
128 // A WebContents in a <webview> (a GuestView type) does not have a password
129 // manager, but still needs to be able to show login prompts.
130 const auto* guest =
131 guest_view::GuestViewBase::FromWebContents(parent_contents);
132 if (guest &&
133 extensions::GetViewType(guest->owner_web_contents()) !=
134 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
135 handler->BuildViewWithoutPasswordManager(authority, explanation);
136 return;
137 }
138 #endif
139 handler->CancelAuth();
140 return;
141 }
142
143 if (password_manager &&
144 password_manager->client()->GetLogManager()->IsLoggingActive()) {
145 password_manager::BrowserSavePasswordProgressLogger logger(
146 password_manager->client()->GetLogManager());
147 logger.LogMessage(
148 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD);
149 }
150
151 PasswordForm observed_form(
152 MakeInputForPasswordManager(request_url, auth_info));
153 handler->BuildViewWithPasswordManager(authority, explanation,
154 password_manager, observed_form);
155 }
156
157 } // namespace 69 } // namespace
158 70
159 // ---------------------------------------------------------------------------- 71 // ----------------------------------------------------------------------------
160 // LoginHandler 72 // LoginHandler
161 73
162 LoginHandler::LoginModelData::LoginModelData( 74 LoginHandler::LoginModelData::LoginModelData(
163 password_manager::LoginModel* login_model, 75 password_manager::LoginModel* login_model,
164 const autofill::PasswordForm& observed_form) 76 const autofill::PasswordForm& observed_form)
165 : model(login_model), form(observed_form) { 77 : model(login_model), form(observed_form) {
166 DCHECK(model); 78 DCHECK(model);
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after
505 } 417 }
506 418
507 // Closes the view_contents from the UI loop. 419 // Closes the view_contents from the UI loop.
508 void LoginHandler::CloseContentsDeferred() { 420 void LoginHandler::CloseContentsDeferred() {
509 DCHECK_CURRENTLY_ON(BrowserThread::UI); 421 DCHECK_CURRENTLY_ON(BrowserThread::UI);
510 CloseDialog(); 422 CloseDialog();
511 if (interstitial_delegate_) 423 if (interstitial_delegate_)
512 interstitial_delegate_->Proceed(); 424 interstitial_delegate_->Proceed();
513 } 425 }
514 426
515 // This callback is run on the UI thread and creates a constrained window with 427 // static
516 // a LoginView to prompt the user. If the prompt is triggered because of 428 std::string LoginHandler::GetSignonRealm(
517 // a cross origin navigation in the main frame, a blank interstitial is first 429 const GURL& url,
518 // created which in turn creates the LoginView. Otherwise, a LoginView is 430 const net::AuthChallengeInfo& auth_info) {
519 // directly in this callback. In both cases, the response will be sent to 431 std::string signon_realm;
520 // LoginHandler, which then routes it to the net::URLRequest on the I/O thread. 432 if (auth_info.is_proxy) {
521 void LoginDialogCallback(const GURL& request_url, 433 // Historically we've been storing the signon realm for proxies using
522 net::AuthChallengeInfo* auth_info, 434 // net::HostPortPair::ToString().
523 LoginHandler* handler, 435 net::HostPortPair host_port_pair =
524 bool is_main_frame) { 436 net::HostPortPair::FromURL(GURL(auth_info.challenger.Serialize()));
437 signon_realm = host_port_pair.ToString();
438 signon_realm.append("/");
439 } else {
440 // Take scheme, host, and port from the url.
441 signon_realm = url.GetOrigin().spec();
442 // This ends with a "/".
443 }
444 signon_realm.append(auth_info.realm);
445 return signon_realm;
446 }
447
448 // static
449 PasswordForm LoginHandler::MakeInputForPasswordManager(
450 const GURL& request_url,
451 const net::AuthChallengeInfo& auth_info) {
452 PasswordForm dialog_form;
453 if (base::LowerCaseEqualsASCII(auth_info.scheme, net::kBasicAuthScheme)) {
454 dialog_form.scheme = PasswordForm::SCHEME_BASIC;
455 } else if (base::LowerCaseEqualsASCII(auth_info.scheme,
456 net::kDigestAuthScheme)) {
457 dialog_form.scheme = PasswordForm::SCHEME_DIGEST;
458 } else {
459 dialog_form.scheme = PasswordForm::SCHEME_OTHER;
460 }
461 if (auth_info.is_proxy) {
462 dialog_form.origin = GURL(auth_info.challenger.Serialize());
463 } else if (auth_info.challenger != url::Origin(request_url)) {
meacer 2016/06/16 01:12:14 Why not use !IsSameOrigin here? It sounds a bit he
asanka 2016/06/16 16:25:47 The addition of the operator follows the style gui
464 dialog_form.origin = GURL();
465 NOTREACHED(); // crbug.com/32718
466 } else {
467 dialog_form.origin = GURL(auth_info.challenger.Serialize());
468 }
469 dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, auth_info);
470 return dialog_form;
471 }
472
473 // static
474 void LoginHandler::GetDialogStrings(const GURL& request_url,
475 const net::AuthChallengeInfo& auth_info,
476 base::string16* authority,
477 base::string16* explanation) {
478 GURL authority_url;
479
480 if (auth_info.is_proxy) {
481 *authority = l10n_util::GetStringFUTF16(
482 IDS_LOGIN_DIALOG_PROXY_AUTHORITY,
483 url_formatter::FormatOriginForSecurityDisplay(
484 auth_info.challenger, url_formatter::SchemeDisplay::SHOW));
485 authority_url = GURL(auth_info.challenger.Serialize());
486 } else {
487 *authority = l10n_util::GetStringFUTF16(
488 IDS_LOGIN_DIALOG_AUTHORITY,
489 url_formatter::FormatUrlForSecurityDisplay(request_url));
490 authority_url = request_url;
491 }
492
493 if (!content::IsOriginSecure(authority_url)) {
494 // TODO(asanka): The string should be different for proxies and servers.
vabr (Chromium) 2016/06/15 08:50:06 nit: Could you please make this a TODO(crbug.com/X
asanka 2016/06/16 16:25:47 Added one. The plan is to pretty much fix it immed
vabr (Chromium) 2016/06/16 16:43:21 Acknowledged.
495 *explanation =
496 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT);
497 } else {
498 explanation->clear();
499 }
500 }
501
502 // static
503 void LoginHandler::ShowLoginPrompt(const GURL& request_url,
504 net::AuthChallengeInfo* auth_info,
505 LoginHandler* handler) {
506 DCHECK_CURRENTLY_ON(BrowserThread::UI);
507 WebContents* parent_contents = handler->GetWebContentsForLogin();
508 if (!parent_contents)
509 return;
510 prerender::PrerenderContents* prerender_contents =
511 prerender::PrerenderContents::FromWebContents(parent_contents);
512 if (prerender_contents) {
513 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED);
514 return;
515 }
516
517 base::string16 authority;
518 base::string16 explanation;
519 GetDialogStrings(request_url, *auth_info, &authority, &explanation);
520
521 password_manager::PasswordManager* password_manager =
522 handler->GetPasswordManagerForLogin();
523
524 if (!password_manager) {
525 #if defined(ENABLE_EXTENSIONS)
526 // A WebContents in a <webview> (a GuestView type) does not have a password
527 // manager, but still needs to be able to show login prompts.
528 const auto* guest =
529 guest_view::GuestViewBase::FromWebContents(parent_contents);
530 if (guest &&
531 extensions::GetViewType(guest->owner_web_contents()) !=
532 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
533 handler->BuildViewWithoutPasswordManager(authority, explanation);
534 return;
535 }
536 #endif
537 handler->CancelAuth();
538 return;
539 }
540
541 if (password_manager &&
542 password_manager->client()->GetLogManager()->IsLoggingActive()) {
543 password_manager::BrowserSavePasswordProgressLogger logger(
544 password_manager->client()->GetLogManager());
545 logger.LogMessage(
546 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD);
547 }
548
549 PasswordForm observed_form(
550 LoginHandler::MakeInputForPasswordManager(request_url, *auth_info));
551 handler->BuildViewWithPasswordManager(authority, explanation,
552 password_manager, observed_form);
553 }
554
555 // static
556 void LoginHandler::LoginDialogCallback(const GURL& request_url,
557 net::AuthChallengeInfo* auth_info,
558 LoginHandler* handler,
559 bool is_main_frame) {
525 DCHECK_CURRENTLY_ON(BrowserThread::UI); 560 DCHECK_CURRENTLY_ON(BrowserThread::UI);
526 WebContents* parent_contents = handler->GetWebContentsForLogin(); 561 WebContents* parent_contents = handler->GetWebContentsForLogin();
527 if (!parent_contents || handler->WasAuthHandled()) { 562 if (!parent_contents || handler->WasAuthHandled()) {
528 // The request may have been canceled, or it may be for a renderer not 563 // The request may have been canceled, or it may be for a renderer not
529 // hosted by a tab (e.g. an extension). Cancel just in case (canceling twice 564 // hosted by a tab (e.g. an extension). Cancel just in case (canceling twice
530 // is a no-op). 565 // is a no-op).
531 handler->CancelAuth(); 566 handler->CancelAuth();
532 return; 567 return;
533 } 568 }
534 569
535 // Check if this is a main frame navigation and 570 // Check if this is a main frame navigation and
536 // (a) if the request is cross origin or 571 // (a) if the request is cross origin or
537 // (b) if an interstitial is already being shown. 572 // (b) if an interstitial is already being shown.
538 // 573 //
539 // For (a), there are two different ways the navigation can occur: 574 // For (a), there are two different ways the navigation can occur:
540 // 1- The user enters the resource URL in the omnibox. 575 // 1- The user enters the resource URL in the omnibox.
541 // 2- The page redirects to the resource. 576 // 2- The page redirects to the resource.
542 // In both cases, the last committed URL is different than the resource URL, 577 // In both cases, the last committed URL is different than the resource URL,
543 // so checking it is sufficient. 578 // so checking it is sufficient.
544 // Note that (1) will not be true once site isolation is enabled, as any 579 // Note that (1) will not be true once site isolation is enabled, as any
545 // navigation could cause a cross-process swap, including link clicks. 580 // navigation could cause a cross-process swap, including link clicks.
546 // 581 //
547 // For (b), the login interstitial should always replace an existing 582 // For (b), the login interstitial should always replace an existing
548 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries 583 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries
549 // to proceed whatever interstitial is being shown when the login dialog is 584 // to proceed whatever interstitial is being shown when the login dialog is
550 // closed, so that interstitial should only be a login interstitial. 585 // closed, so that interstitial should only be a login interstitial.
551 if (is_main_frame && (parent_contents->ShowingInterstitialPage() || 586 if (is_main_frame &&
552 parent_contents->GetLastCommittedURL().GetOrigin() != 587 (parent_contents->ShowingInterstitialPage() || auth_info->is_proxy ||
meacer 2016/06/16 01:12:14 nit: Can you update the comment above and add the
asanka 2016/06/16 16:25:47 Done. Thanks, missed the comment in the first pass
553 request_url.GetOrigin())) { 588 parent_contents->GetLastCommittedURL().GetOrigin() !=
589 request_url.GetOrigin())) {
554 // Show a blank interstitial for main-frame, cross origin requests 590 // Show a blank interstitial for main-frame, cross origin requests
555 // so that the correct URL is shown in the omnibox. 591 // so that the correct URL is shown in the omnibox.
556 base::Closure callback = 592 base::Closure callback =
557 base::Bind(&ShowLoginPrompt, request_url, base::RetainedRef(auth_info), 593 base::Bind(&LoginHandler::ShowLoginPrompt, request_url,
558 base::RetainedRef(handler)); 594 base::RetainedRef(auth_info), base::RetainedRef(handler));
559 // The interstitial delegate is owned by the interstitial that it creates. 595 // The interstitial delegate is owned by the interstitial that it creates.
560 // This cancels any existing interstitial. 596 // This cancels any existing interstitial.
561 handler->SetInterstitialDelegate( 597 handler->SetInterstitialDelegate(
562 (new LoginInterstitialDelegate(parent_contents, request_url, callback)) 598 (new LoginInterstitialDelegate(
599 parent_contents, auth_info->is_proxy ? GURL() : request_url,
600 callback))
563 ->GetWeakPtr()); 601 ->GetWeakPtr());
564 } else { 602 } else {
565 ShowLoginPrompt(request_url, auth_info, handler); 603 ShowLoginPrompt(request_url, auth_info, handler);
566 } 604 }
567 } 605 }
568 606
569 // ---------------------------------------------------------------------------- 607 // ----------------------------------------------------------------------------
570 // Public API 608 // Public API
571 609
572 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, 610 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
573 net::URLRequest* request) { 611 net::URLRequest* request) {
574 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; 612 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0;
575 LoginHandler* handler = LoginHandler::Create(auth_info, request); 613 LoginHandler* handler = LoginHandler::Create(auth_info, request);
576 BrowserThread::PostTask( 614 BrowserThread::PostTask(
577 BrowserThread::UI, FROM_HERE, 615 BrowserThread::UI, FROM_HERE,
578 base::Bind(&LoginDialogCallback, request->url(), 616 base::Bind(&LoginHandler::LoginDialogCallback, request->url(),
579 base::RetainedRef(auth_info), base::RetainedRef(handler), 617 base::RetainedRef(auth_info), base::RetainedRef(handler),
580 is_main_frame)); 618 is_main_frame));
581 return handler; 619 return handler;
582 } 620 }
583 621
584 // Get the signon_realm under which this auth info should be stored.
585 //
586 // The format of the signon_realm for proxy auth is:
587 // proxy-host/auth-realm
588 // The format of the signon_realm for server auth is:
589 // url-scheme://url-host[:url-port]/auth-realm
590 //
591 // Be careful when changing this function, since you could make existing
592 // saved logins un-retrievable.
593 std::string GetSignonRealm(const GURL& url,
594 const net::AuthChallengeInfo& auth_info) {
595 std::string signon_realm;
596 if (auth_info.is_proxy) {
597 signon_realm = auth_info.challenger.ToString();
598 signon_realm.append("/");
599 } else {
600 // Take scheme, host, and port from the url.
601 signon_realm = url.GetOrigin().spec();
602 // This ends with a "/".
603 }
604 signon_realm.append(auth_info.realm);
605 return signon_realm;
606 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698