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