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

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: Fix content_shell build. 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;
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);
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.IsSameOriginWith(url::Origin(request_url))) {
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.
495 // http://crbug.com/620756
496 *explanation =
497 l10n_util::GetStringUTF16(IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT);
498 } else {
499 explanation->clear();
500 }
501 }
502
503 // static
504 void LoginHandler::ShowLoginPrompt(const GURL& request_url,
505 net::AuthChallengeInfo* auth_info,
506 LoginHandler* handler) {
507 DCHECK_CURRENTLY_ON(BrowserThread::UI);
508 WebContents* parent_contents = handler->GetWebContentsForLogin();
509 if (!parent_contents)
510 return;
511 prerender::PrerenderContents* prerender_contents =
512 prerender::PrerenderContents::FromWebContents(parent_contents);
513 if (prerender_contents) {
514 prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED);
515 return;
516 }
517
518 base::string16 authority;
519 base::string16 explanation;
520 GetDialogStrings(request_url, *auth_info, &authority, &explanation);
521
522 password_manager::PasswordManager* password_manager =
523 handler->GetPasswordManagerForLogin();
524
525 if (!password_manager) {
526 #if defined(ENABLE_EXTENSIONS)
527 // A WebContents in a <webview> (a GuestView type) does not have a password
528 // manager, but still needs to be able to show login prompts.
529 const auto* guest =
530 guest_view::GuestViewBase::FromWebContents(parent_contents);
531 if (guest &&
532 extensions::GetViewType(guest->owner_web_contents()) !=
533 extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
534 handler->BuildViewWithoutPasswordManager(authority, explanation);
535 return;
536 }
537 #endif
538 handler->CancelAuth();
539 return;
540 }
541
542 if (password_manager &&
543 password_manager->client()->GetLogManager()->IsLoggingActive()) {
544 password_manager::BrowserSavePasswordProgressLogger logger(
545 password_manager->client()->GetLogManager());
546 logger.LogMessage(
547 autofill::SavePasswordProgressLogger::STRING_SHOW_LOGIN_PROMPT_METHOD);
548 }
549
550 PasswordForm observed_form(
551 LoginHandler::MakeInputForPasswordManager(request_url, *auth_info));
552 handler->BuildViewWithPasswordManager(authority, explanation,
553 password_manager, observed_form);
554 }
555
556 // static
557 void LoginHandler::LoginDialogCallback(const GURL& request_url,
558 net::AuthChallengeInfo* auth_info,
559 LoginHandler* handler,
560 bool is_main_frame) {
525 DCHECK_CURRENTLY_ON(BrowserThread::UI); 561 DCHECK_CURRENTLY_ON(BrowserThread::UI);
526 WebContents* parent_contents = handler->GetWebContentsForLogin(); 562 WebContents* parent_contents = handler->GetWebContentsForLogin();
527 if (!parent_contents || handler->WasAuthHandled()) { 563 if (!parent_contents || handler->WasAuthHandled()) {
528 // The request may have been canceled, or it may be for a renderer not 564 // 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 565 // hosted by a tab (e.g. an extension). Cancel just in case (canceling twice
530 // is a no-op). 566 // is a no-op).
531 handler->CancelAuth(); 567 handler->CancelAuth();
532 return; 568 return;
533 } 569 }
534 570
535 // Check if this is a main frame navigation and 571 // Check if this is a main frame navigation and
536 // (a) if the request is cross origin or 572 // (a) if the request is cross origin or
537 // (b) if an interstitial is already being shown. 573 // (b) if an interstitial is already being shown or
574 // (c) the prompt is for proxy authentication
538 // 575 //
539 // For (a), there are two different ways the navigation can occur: 576 // For (a), there are two different ways the navigation can occur:
540 // 1- The user enters the resource URL in the omnibox. 577 // 1- The user enters the resource URL in the omnibox.
541 // 2- The page redirects to the resource. 578 // 2- The page redirects to the resource.
542 // In both cases, the last committed URL is different than the resource URL, 579 // In both cases, the last committed URL is different than the resource URL,
543 // so checking it is sufficient. 580 // so checking it is sufficient.
544 // Note that (1) will not be true once site isolation is enabled, as any 581 // 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. 582 // navigation could cause a cross-process swap, including link clicks.
546 // 583 //
547 // For (b), the login interstitial should always replace an existing 584 // For (b), the login interstitial should always replace an existing
548 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries 585 // interstitial. This is because |LoginHandler::CloseContentsDeferred| tries
549 // to proceed whatever interstitial is being shown when the login dialog is 586 // to proceed whatever interstitial is being shown when the login dialog is
550 // closed, so that interstitial should only be a login interstitial. 587 // closed, so that interstitial should only be a login interstitial.
551 if (is_main_frame && (parent_contents->ShowingInterstitialPage() || 588 //
552 parent_contents->GetLastCommittedURL().GetOrigin() != 589 // For (c), the authority information in the omnibox will be (and should be)
553 request_url.GetOrigin())) { 590 // different from the authority information in the authentication prompt. An
591 // interstitial with an empty URL clears the omnibox and reduces the possible
592 // user confusion that may result from the different authority information
593 // being displayed simultaneously. This is specially important when the proxy
594 // is accessed via an open connection while the target server is considered
595 // secure.
596 if (is_main_frame &&
597 (parent_contents->ShowingInterstitialPage() || auth_info->is_proxy ||
598 parent_contents->GetLastCommittedURL().GetOrigin() !=
599 request_url.GetOrigin())) {
554 // Show a blank interstitial for main-frame, cross origin requests 600 // Show a blank interstitial for main-frame, cross origin requests
555 // so that the correct URL is shown in the omnibox. 601 // so that the correct URL is shown in the omnibox.
556 base::Closure callback = 602 base::Closure callback =
557 base::Bind(&ShowLoginPrompt, request_url, base::RetainedRef(auth_info), 603 base::Bind(&LoginHandler::ShowLoginPrompt, request_url,
558 base::RetainedRef(handler)); 604 base::RetainedRef(auth_info), base::RetainedRef(handler));
559 // The interstitial delegate is owned by the interstitial that it creates. 605 // The interstitial delegate is owned by the interstitial that it creates.
560 // This cancels any existing interstitial. 606 // This cancels any existing interstitial.
561 handler->SetInterstitialDelegate( 607 handler->SetInterstitialDelegate(
562 (new LoginInterstitialDelegate(parent_contents, request_url, callback)) 608 (new LoginInterstitialDelegate(
609 parent_contents, auth_info->is_proxy ? GURL() : request_url,
610 callback))
563 ->GetWeakPtr()); 611 ->GetWeakPtr());
564 } else { 612 } else {
565 ShowLoginPrompt(request_url, auth_info, handler); 613 ShowLoginPrompt(request_url, auth_info, handler);
566 } 614 }
567 } 615 }
568 616
569 // ---------------------------------------------------------------------------- 617 // ----------------------------------------------------------------------------
570 // Public API 618 // Public API
571 619
572 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info, 620 LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
573 net::URLRequest* request) { 621 net::URLRequest* request) {
574 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0; 622 bool is_main_frame = (request->load_flags() & net::LOAD_MAIN_FRAME) != 0;
575 LoginHandler* handler = LoginHandler::Create(auth_info, request); 623 LoginHandler* handler = LoginHandler::Create(auth_info, request);
576 BrowserThread::PostTask( 624 BrowserThread::PostTask(
577 BrowserThread::UI, FROM_HERE, 625 BrowserThread::UI, FROM_HERE,
578 base::Bind(&LoginDialogCallback, request->url(), 626 base::Bind(&LoginHandler::LoginDialogCallback, request->url(),
579 base::RetainedRef(auth_info), base::RetainedRef(handler), 627 base::RetainedRef(auth_info), base::RetainedRef(handler),
580 is_main_frame)); 628 is_main_frame));
581 return handler; 629 return handler;
582 } 630 }
583 631
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
« no previous file with comments | « chrome/browser/ui/login/login_handler.h ('k') | chrome/browser/ui/login/login_handler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698