1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsSecureBrowserUI.h"
7 
8 #include "mozilla/Assertions.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/Unused.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsContentUtils.h"
13 #include "nsIChannel.h"
14 #include "nsDocShell.h"
15 #include "nsIDocShellTreeItem.h"
16 #include "nsGlobalWindow.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsITransportSecurityInfo.h"
19 #include "nsIWebProgress.h"
20 #include "nsNetUtil.h"
21 #include "mozilla/dom/CanonicalBrowsingContext.h"
22 #include "mozilla/dom/WindowGlobalParent.h"
23 #include "mozilla/dom/Element.h"
24 #include "nsIBrowser.h"
25 
26 using namespace mozilla;
27 using namespace mozilla::dom;
28 
29 LazyLogModule gSecureBrowserUILog("nsSecureBrowserUI");
30 
nsSecureBrowserUI(CanonicalBrowsingContext * aBrowsingContext)31 nsSecureBrowserUI::nsSecureBrowserUI(CanonicalBrowsingContext* aBrowsingContext)
32     : mState(0) {
33   MOZ_ASSERT(NS_IsMainThread());
34 
35   // The BrowsingContext will own the SecureBrowserUI object, we keep a weak
36   // ref.
37   mBrowsingContextId = aBrowsingContext->Id();
38 }
39 
NS_IMPL_ISUPPORTS(nsSecureBrowserUI,nsISecureBrowserUI,nsISupportsWeakReference)40 NS_IMPL_ISUPPORTS(nsSecureBrowserUI, nsISecureBrowserUI,
41                   nsISupportsWeakReference)
42 
43 NS_IMETHODIMP
44 nsSecureBrowserUI::GetState(uint32_t* aState) {
45   MOZ_ASSERT(NS_IsMainThread());
46   NS_ENSURE_ARG(aState);
47 
48   MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
49           ("GetState %p mState: %x", this, mState));
50   *aState = mState;
51   return NS_OK;
52 }
53 
GetWebProgressListener(CanonicalBrowsingContext * aBrowsingContext,nsIBrowser ** aOutBrowser,nsIWebProgress ** aOutManager,nsIWebProgressListener ** aOutListener)54 static bool GetWebProgressListener(CanonicalBrowsingContext* aBrowsingContext,
55                                    nsIBrowser** aOutBrowser,
56                                    nsIWebProgress** aOutManager,
57                                    nsIWebProgressListener** aOutListener) {
58   MOZ_ASSERT(aOutBrowser);
59   MOZ_ASSERT(aOutManager);
60   MOZ_ASSERT(aOutListener);
61 
62   nsCOMPtr<nsIBrowser> browser;
63   RefPtr<Element> currentElement = aBrowsingContext->GetEmbedderElement();
64 
65   // In Responsive Design Mode, mFrameElement will be the <iframe mozbrowser>,
66   // but we want the <xul:browser> that it is embedded in.
67   while (currentElement) {
68     browser = currentElement->AsBrowser();
69     if (browser) {
70       break;
71     }
72 
73     BrowsingContext* browsingContext =
74         currentElement->OwnerDoc()->GetBrowsingContext();
75     currentElement =
76         browsingContext ? browsingContext->GetEmbedderElement() : nullptr;
77   }
78 
79   if (!browser) {
80     return false;
81   }
82 
83   nsCOMPtr<nsIWebProgress> manager;
84   nsresult rv = browser->GetRemoteWebProgressManager(getter_AddRefs(manager));
85   if (NS_FAILED(rv)) {
86     browser.forget(aOutBrowser);
87     return true;
88   }
89 
90   nsCOMPtr<nsIWebProgressListener> listener = do_QueryInterface(manager);
91   if (!listener) {
92     // We are no longer remote so we cannot forward this event.
93     browser.forget(aOutBrowser);
94     manager.forget(aOutManager);
95     return true;
96   }
97 
98   browser.forget(aOutBrowser);
99   manager.forget(aOutManager);
100   listener.forget(aOutListener);
101 
102   return true;
103 }
104 
UpdateForLocationOrMixedContentChange()105 void nsSecureBrowserUI::UpdateForLocationOrMixedContentChange() {
106   // Our BrowsingContext either has a new WindowGlobalParent, or the
107   // existing one has mutated its security state.
108   // Recompute our security state and fire notifications to listeners
109 
110   RefPtr<WindowGlobalParent> win = GetCurrentWindow();
111   mState = nsIWebProgressListener::STATE_IS_INSECURE;
112 
113   // Only https is considered secure (it is possible to have e.g. an http URI
114   // with a channel that has a securityInfo that indicates the connection is
115   // secure - e.g. h2/alt-svc or by visiting an http URI over an https proxy).
116   nsCOMPtr<nsITransportSecurityInfo> securityInfo;
117   if (win && win->GetIsSecure()) {
118     securityInfo = win->GetSecurityInfo();
119     if (securityInfo) {
120       MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
121               ("  we have a security info %p", securityInfo.get()));
122 
123       nsresult rv = securityInfo->GetSecurityState(&mState);
124 
125       // If the security state is STATE_IS_INSECURE, the TLS handshake never
126       // completed. Don't set any further state.
127       if (NS_SUCCEEDED(rv) &&
128           mState != nsIWebProgressListener::STATE_IS_INSECURE) {
129         MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
130                 ("  set mTopLevelSecurityInfo"));
131         bool isEV;
132         rv = securityInfo->GetIsExtendedValidation(&isEV);
133         if (NS_SUCCEEDED(rv) && isEV) {
134           MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  is EV"));
135           mState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
136         }
137       }
138     }
139   }
140 
141   // Add the mixed content flags from the window
142   if (win) {
143     mState |= win->GetMixedContentSecurityFlags();
144   }
145 
146   // If we have loaded mixed content and this is a secure page,
147   // then clear secure flags and add broken instead.
148   static const uint32_t kLoadedMixedContentFlags =
149       nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
150       nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
151   if (win && win->GetIsSecure() && (mState & kLoadedMixedContentFlags)) {
152     // reset state security flag
153     mState = mState >> 4 << 4;
154     // set state security flag to broken, since there is mixed content
155     mState |= nsIWebProgressListener::STATE_IS_BROKEN;
156   }
157 
158   RefPtr<CanonicalBrowsingContext> ctx =
159       CanonicalBrowsingContext::Get(mBrowsingContextId);
160   if (!ctx) {
161     return;
162   }
163 
164   // This is a bit painful, as we need to do different things for
165   // in-process docshells vs OOP ones.
166   // Ideally we'd just call a function on 'browser' which would
167   // handle sending an event to all listeners, and we wouldn't
168   // need to bother with onSecurityChange.
169   nsCOMPtr<nsIBrowser> browser;
170   nsCOMPtr<nsIWebProgress> manager;
171   nsCOMPtr<nsIWebProgressListener> managerAsListener;
172   if (!GetWebProgressListener(ctx, getter_AddRefs(browser),
173                               getter_AddRefs(manager),
174                               getter_AddRefs(managerAsListener))) {
175     return;
176   }
177 
178   // Do we need to construct a fake webprogress and request instance?
179   // Should we split this API out of nsIWebProgressListener to avoid
180   // that?
181   if (managerAsListener) {
182     Unused << managerAsListener->OnSecurityChange(nullptr, nullptr, mState);
183   }
184   if (ctx->GetDocShell()) {
185     nsDocShell* nativeDocShell = nsDocShell::Cast(ctx->GetDocShell());
186     nativeDocShell->nsDocLoader::OnSecurityChange(nullptr, mState);
187   }
188 }
189 
190 NS_IMETHODIMP
GetIsSecureContext(bool * aIsSecureContext)191 nsSecureBrowserUI::GetIsSecureContext(bool* aIsSecureContext) {
192   MOZ_ASSERT(NS_IsMainThread());
193   NS_ENSURE_ARG(aIsSecureContext);
194 
195   if (WindowGlobalParent* parent = GetCurrentWindow()) {
196     *aIsSecureContext = parent->GetIsSecureContext();
197   } else {
198     *aIsSecureContext = false;
199   }
200   return NS_OK;
201 }
202 
203 NS_IMETHODIMP
GetSecInfo(nsITransportSecurityInfo ** result)204 nsSecureBrowserUI::GetSecInfo(nsITransportSecurityInfo** result) {
205   MOZ_ASSERT(NS_IsMainThread());
206   NS_ENSURE_ARG_POINTER(result);
207 
208   if (WindowGlobalParent* parent = GetCurrentWindow()) {
209     *result = parent->GetSecurityInfo();
210   }
211   NS_IF_ADDREF(*result);
212 
213   return NS_OK;
214 }
215 
GetCurrentWindow()216 WindowGlobalParent* nsSecureBrowserUI::GetCurrentWindow() {
217   RefPtr<CanonicalBrowsingContext> ctx =
218       CanonicalBrowsingContext::Get(mBrowsingContextId);
219   if (!ctx) {
220     return nullptr;
221   }
222   return ctx->GetCurrentWindowGlobal();
223 }
224