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 "nsParentalControlsService.h"
7 #include "nsComponentManagerUtils.h"
8 #include "nsString.h"
9 #include "nsIArray.h"
10 #include "nsIWidget.h"
11 #include "nsIInterfaceRequestor.h"
12 #include "nsIInterfaceRequestorUtils.h"
13 #include "nsIFile.h"
14 #include "nsILocalFileWin.h"
15 #include "nsArrayUtils.h"
16 #include "nsIXULAppInfo.h"
17 #include "nsWindowsHelpers.h"
18 #include "nsIWindowsRegKey.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/WindowsVersion.h"
21 
22 #include <sddl.h>
23 
24 using namespace mozilla;
25 
NS_IMPL_ISUPPORTS(nsParentalControlsService,nsIParentalControlsService)26 NS_IMPL_ISUPPORTS(nsParentalControlsService, nsIParentalControlsService)
27 
28 // Get the SID string for the user associated with this process's token.
29 static nsAutoString GetUserSid() {
30   nsAutoString ret;
31   HANDLE rawToken;
32   BOOL success =
33       ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
34   if (!success) {
35     return ret;
36   }
37   nsAutoHandle token(rawToken);
38 
39   DWORD bufLen;
40   success = ::GetTokenInformation(token, TokenUser, nullptr, 0, &bufLen);
41   if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
42     return ret;
43   }
44 
45   UniquePtr<char[]> buf = MakeUnique<char[]>(bufLen);
46   success = ::GetTokenInformation(token, TokenUser, buf.get(), bufLen, &bufLen);
47   MOZ_ASSERT(success);
48 
49   if (success) {
50     TOKEN_USER* tokenUser = (TOKEN_USER*)(buf.get());
51     PSID sid = tokenUser->User.Sid;
52     LPWSTR sidStr;
53     success = ::ConvertSidToStringSidW(sid, &sidStr);
54     if (success) {
55       ret = sidStr;
56       ::LocalFree(sidStr);
57     }
58   }
59   return ret;
60 }
61 
nsParentalControlsService()62 nsParentalControlsService::nsParentalControlsService()
63     : mEnabled(false), mProvider(0), mPC(nullptr) {
64   // On at least some builds of Windows 10, the old parental controls API no
65   // longer exists, so we have to pull the info we need out of the registry.
66   if (IsWin10OrLater()) {
67     nsAutoString regKeyName;
68     regKeyName.AppendLiteral(
69         "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Parental Controls\\"
70         "Users\\");
71     regKeyName.Append(GetUserSid());
72     regKeyName.AppendLiteral("\\Web");
73 
74     nsresult rv;
75     nsCOMPtr<nsIWindowsRegKey> regKey =
76         do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
77     if (NS_FAILED(rv)) {
78       return;
79     }
80 
81     rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, regKeyName,
82                       nsIWindowsRegKey::ACCESS_READ);
83     if (NS_FAILED(rv)) {
84       return;
85     }
86 
87     uint32_t filterOn = 0;
88     rv = regKey->ReadIntValue(NS_LITERAL_STRING("Filter On"), &filterOn);
89     if (NS_FAILED(rv)) {
90       return;
91     }
92 
93     mEnabled = filterOn != 0;
94     return;
95   }
96 
97   HRESULT hr;
98   CoInitialize(nullptr);
99   hr = CoCreateInstance(__uuidof(WindowsParentalControls), nullptr,
100                         CLSCTX_INPROC, IID_PPV_ARGS(&mPC));
101   if (FAILED(hr)) return;
102 
103   RefPtr<IWPCSettings> wpcs;
104   if (FAILED(mPC->GetUserSettings(nullptr, getter_AddRefs(wpcs)))) {
105     // Not available on this os or not enabled for this user account or we're
106     // running as admin
107     mPC->Release();
108     mPC = nullptr;
109     return;
110   }
111 
112   DWORD settings = 0;
113   wpcs->GetRestrictions(&settings);
114 
115   // If we can't determine specifically whether Web Filtering is on/off (i.e.
116   // we're on Windows < 8), then assume it's on unless no restrictions are set.
117   bool enable = IsWin8OrLater() ? settings & WPCFLAG_WEB_FILTERED
118                                 : settings != WPCFLAG_NO_RESTRICTION;
119 
120   if (enable) {
121     mEnabled = true;
122   }
123 }
124 
~nsParentalControlsService()125 nsParentalControlsService::~nsParentalControlsService() {
126   if (mPC) mPC->Release();
127 
128   if (mProvider) {
129     EventUnregister(mProvider);
130   }
131 }
132 
133 //------------------------------------------------------------------------
134 
135 NS_IMETHODIMP
GetParentalControlsEnabled(bool * aResult)136 nsParentalControlsService::GetParentalControlsEnabled(bool* aResult) {
137   *aResult = false;
138 
139   if (mEnabled) *aResult = true;
140 
141   return NS_OK;
142 }
143 
144 NS_IMETHODIMP
GetBlockFileDownloadsEnabled(bool * aResult)145 nsParentalControlsService::GetBlockFileDownloadsEnabled(bool* aResult) {
146   *aResult = false;
147 
148   if (!mEnabled) {
149     return NS_ERROR_NOT_AVAILABLE;
150   }
151 
152   // If we're on Windows 10 and we don't have the whole API available, then
153   // we can't tell if file downloads are allowed, so assume they are to avoid
154   // breaking downloads for every single user with parental controls.
155   if (!mPC) {
156     return NS_OK;
157   }
158 
159   RefPtr<IWPCWebSettings> wpcws;
160   if (SUCCEEDED(mPC->GetWebSettings(nullptr, getter_AddRefs(wpcws)))) {
161     DWORD settings = 0;
162     wpcws->GetSettings(&settings);
163     if (settings == WPCFLAG_WEB_SETTING_DOWNLOADSBLOCKED) *aResult = true;
164   }
165 
166   return NS_OK;
167 }
168 
169 NS_IMETHODIMP
GetLoggingEnabled(bool * aResult)170 nsParentalControlsService::GetLoggingEnabled(bool* aResult) {
171   *aResult = false;
172 
173   if (!mEnabled) {
174     return NS_ERROR_NOT_AVAILABLE;
175   }
176 
177   // If we're on Windows 10 and we don't have the whole API available, then
178   // we don't know how logging should be done, so report that it's disabled.
179   if (!mPC) {
180     return NS_OK;
181   }
182 
183   // Check the general purpose logging flag
184   RefPtr<IWPCSettings> wpcs;
185   if (SUCCEEDED(mPC->GetUserSettings(nullptr, getter_AddRefs(wpcs)))) {
186     BOOL enabled = FALSE;
187     wpcs->IsLoggingRequired(&enabled);
188     if (enabled) *aResult = true;
189   }
190 
191   return NS_OK;
192 }
193 
194 // Post a log event to the system
195 NS_IMETHODIMP
Log(int16_t aEntryType,bool blocked,nsIURI * aSource,nsIFile * aTarget)196 nsParentalControlsService::Log(int16_t aEntryType, bool blocked,
197                                nsIURI* aSource, nsIFile* aTarget) {
198   if (!mEnabled) return NS_ERROR_NOT_AVAILABLE;
199 
200   NS_ENSURE_ARG_POINTER(aSource);
201 
202   // Confirm we should be logging
203   bool enabled;
204   GetLoggingEnabled(&enabled);
205   if (!enabled) return NS_ERROR_NOT_AVAILABLE;
206 
207   // Register a Vista log event provider associated with the parental controls
208   // channel.
209   if (!mProvider) {
210     if (EventRegister(&WPCPROV, nullptr, nullptr, &mProvider) != ERROR_SUCCESS)
211       return NS_ERROR_OUT_OF_MEMORY;
212   }
213 
214   switch (aEntryType) {
215     case ePCLog_URIVisit:
216       // Not needed, Vista's web content filter handles this for us
217       break;
218     case ePCLog_FileDownload:
219       LogFileDownload(blocked, aSource, aTarget);
220       break;
221     default:
222       break;
223   }
224 
225   return NS_OK;
226 }
227 
228 //------------------------------------------------------------------------
229 
230 // Sends a file download event to the Vista Event Log
LogFileDownload(bool blocked,nsIURI * aSource,nsIFile * aTarget)231 void nsParentalControlsService::LogFileDownload(bool blocked, nsIURI* aSource,
232                                                 nsIFile* aTarget) {
233   nsAutoCString curi;
234 
235   // Note, EventDataDescCreate is a macro defined in the headers, not a function
236 
237   aSource->GetSpec(curi);
238   nsAutoString uri = NS_ConvertUTF8toUTF16(curi);
239 
240   // Get the name of the currently running process
241   nsCOMPtr<nsIXULAppInfo> appInfo =
242       do_GetService("@mozilla.org/xre/app-info;1");
243   nsAutoCString asciiAppName;
244   if (appInfo) appInfo->GetName(asciiAppName);
245   nsAutoString appName = NS_ConvertUTF8toUTF16(asciiAppName);
246 
247   static const WCHAR fill[] = L"";
248 
249   // See wpcevent.h and msdn for event formats
250   EVENT_DATA_DESCRIPTOR eventData[WPC_ARGS_FILEDOWNLOADEVENT_CARGS];
251   DWORD dwBlocked = blocked;
252 
253   EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_URL],
254                       (const void*)uri.get(),
255                       ((ULONG)uri.Length() + 1) * sizeof(WCHAR));
256   EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_APPNAME],
257                       (const void*)appName.get(),
258                       ((ULONG)appName.Length() + 1) * sizeof(WCHAR));
259   EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_VERSION],
260                       (const void*)fill, sizeof(fill));
261   EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_BLOCKED],
262                       (const void*)&dwBlocked, sizeof(dwBlocked));
263 
264   nsCOMPtr<nsILocalFileWin> local(do_QueryInterface(aTarget));  // May be null
265   if (local) {
266     nsAutoString path;
267     local->GetCanonicalPath(path);
268     EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_PATH],
269                         (const void*)path.get(),
270                         ((ULONG)path.Length() + 1) * sizeof(WCHAR));
271   } else {
272     EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_PATH],
273                         (const void*)fill, sizeof(fill));
274   }
275 
276   EventWrite(mProvider, &WPCEVENT_WEB_FILEDOWNLOAD, ARRAYSIZE(eventData),
277              eventData);
278 }
279 
280 NS_IMETHODIMP
IsAllowed(int16_t aAction,nsIURI * aUri,bool * _retval)281 nsParentalControlsService::IsAllowed(int16_t aAction, nsIURI* aUri,
282                                      bool* _retval) {
283   return NS_ERROR_NOT_AVAILABLE;
284 }
285