|
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 "mozilla/Attributes.h" |
|
7 #include "mozilla/DebugOnly.h" |
|
8 |
|
9 #include "mozilla/dom/ContentParent.h" |
|
10 #include "mozilla/dom/ContentChild.h" |
|
11 #include "mozilla/unused.h" |
|
12 #include "nsPermissionManager.h" |
|
13 #include "nsPermission.h" |
|
14 #include "nsCRT.h" |
|
15 #include "nsNetUtil.h" |
|
16 #include "nsCOMArray.h" |
|
17 #include "nsArrayEnumerator.h" |
|
18 #include "nsTArray.h" |
|
19 #include "nsReadableUtils.h" |
|
20 #include "nsILineInputStream.h" |
|
21 #include "nsIIDNService.h" |
|
22 #include "nsAppDirectoryServiceDefs.h" |
|
23 #include "prprf.h" |
|
24 #include "mozilla/storage.h" |
|
25 #include "mozilla/Attributes.h" |
|
26 #include "nsXULAppAPI.h" |
|
27 #include "nsIPrincipal.h" |
|
28 #include "nsContentUtils.h" |
|
29 #include "nsIScriptSecurityManager.h" |
|
30 #include "nsIAppsService.h" |
|
31 #include "mozIApplication.h" |
|
32 #include "nsIEffectiveTLDService.h" |
|
33 #include "nsPIDOMWindow.h" |
|
34 #include "nsIDocument.h" |
|
35 #include "nsCOMPtr.h" |
|
36 #include "nsIPrefService.h" |
|
37 #include "nsIPrefBranch.h" |
|
38 #include "nsIPrefBranch2.h" |
|
39 #include "mozilla/net/NeckoMessageUtils.h" |
|
40 |
|
41 static nsPermissionManager *gPermissionManager = nullptr; |
|
42 |
|
43 using mozilla::dom::ContentParent; |
|
44 using mozilla::dom::ContentChild; |
|
45 using mozilla::unused; // ha! |
|
46 |
|
47 static bool |
|
48 IsChildProcess() |
|
49 { |
|
50 return XRE_GetProcessType() == GeckoProcessType_Content; |
|
51 } |
|
52 |
|
53 /** |
|
54 * @returns The child process object, or if we are not in the child |
|
55 * process, nullptr. |
|
56 */ |
|
57 static ContentChild* |
|
58 ChildProcess() |
|
59 { |
|
60 if (IsChildProcess()) { |
|
61 ContentChild* cpc = ContentChild::GetSingleton(); |
|
62 if (!cpc) |
|
63 NS_RUNTIMEABORT("Content Process is nullptr!"); |
|
64 return cpc; |
|
65 } |
|
66 |
|
67 return nullptr; |
|
68 } |
|
69 |
|
70 |
|
71 #define ENSURE_NOT_CHILD_PROCESS_(onError) \ |
|
72 PR_BEGIN_MACRO \ |
|
73 if (IsChildProcess()) { \ |
|
74 NS_ERROR("Cannot perform action in content process!"); \ |
|
75 onError \ |
|
76 } \ |
|
77 PR_END_MACRO |
|
78 |
|
79 #define ENSURE_NOT_CHILD_PROCESS \ |
|
80 ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; }) |
|
81 |
|
82 #define ENSURE_NOT_CHILD_PROCESS_NORET \ |
|
83 ENSURE_NOT_CHILD_PROCESS_(;) |
|
84 |
|
85 //////////////////////////////////////////////////////////////////////////////// |
|
86 |
|
87 namespace { |
|
88 |
|
89 nsresult |
|
90 GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement, |
|
91 nsIPrincipal** aPrincipal) |
|
92 { |
|
93 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
|
94 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); |
|
95 |
|
96 nsCOMPtr<nsIURI> uri; |
|
97 nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); |
|
98 if (NS_FAILED(rv)) { |
|
99 // NOTE: most callers will end up here because we don't append "http://" for |
|
100 // hosts. It's fine to arbitrary use "http://" because, for those entries, |
|
101 // we will actually just use the host. If we end up here, but the host looks |
|
102 // like an email address, we use mailto: instead. |
|
103 nsCString scheme; |
|
104 if (aHost.FindChar('@') == -1) |
|
105 scheme = NS_LITERAL_CSTRING("http://"); |
|
106 else |
|
107 scheme = NS_LITERAL_CSTRING("mailto:"); |
|
108 rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost); |
|
109 NS_ENSURE_SUCCESS(rv, rv); |
|
110 } |
|
111 |
|
112 return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal); |
|
113 } |
|
114 |
|
115 nsresult |
|
116 GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) |
|
117 { |
|
118 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
|
119 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); |
|
120 |
|
121 return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal); |
|
122 } |
|
123 |
|
124 nsresult |
|
125 GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal) |
|
126 { |
|
127 return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal); |
|
128 } |
|
129 |
|
130 nsresult |
|
131 GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost) |
|
132 { |
|
133 nsCOMPtr<nsIURI> uri; |
|
134 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); |
|
135 NS_ENSURE_SUCCESS(rv, rv); |
|
136 |
|
137 uri = NS_GetInnermostURI(uri); |
|
138 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); |
|
139 |
|
140 rv = uri->GetAsciiHost(aHost); |
|
141 if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { |
|
142 return NS_OK; |
|
143 } |
|
144 |
|
145 // For the mailto scheme, we use the path of the URI. We have to chop off the |
|
146 // query part if one exists, so we eliminate everything after a ?. |
|
147 bool isMailTo = false; |
|
148 if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) { |
|
149 rv = uri->GetPath(aHost); |
|
150 NS_ENSURE_SUCCESS(rv, rv); |
|
151 |
|
152 int32_t spart = aHost.FindChar('?', 0); |
|
153 if (spart >= 0) { |
|
154 aHost.Cut(spart, aHost.Length() - spart); |
|
155 } |
|
156 return NS_OK; |
|
157 } |
|
158 |
|
159 // Some entries like "file://" uses the origin. |
|
160 rv = aPrincipal->GetOrigin(getter_Copies(aHost)); |
|
161 if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { |
|
162 return NS_OK; |
|
163 } |
|
164 |
|
165 return NS_ERROR_UNEXPECTED; |
|
166 } |
|
167 |
|
168 nsCString |
|
169 GetNextSubDomainForHost(const nsACString& aHost) |
|
170 { |
|
171 nsCOMPtr<nsIEffectiveTLDService> tldService = |
|
172 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); |
|
173 if (!tldService) { |
|
174 NS_ERROR("Should have a tld service!"); |
|
175 return EmptyCString(); |
|
176 } |
|
177 |
|
178 nsCString subDomain; |
|
179 nsresult rv = tldService->GetNextSubDomain(aHost, subDomain); |
|
180 // We can fail if there is no more subdomain or if the host can't have a |
|
181 // subdomain. |
|
182 if (NS_FAILED(rv)) { |
|
183 return EmptyCString(); |
|
184 } |
|
185 |
|
186 return subDomain; |
|
187 } |
|
188 |
|
189 class AppClearDataObserver MOZ_FINAL : public nsIObserver { |
|
190 public: |
|
191 NS_DECL_ISUPPORTS |
|
192 |
|
193 // nsIObserver implementation. |
|
194 NS_IMETHODIMP |
|
195 Observe(nsISupports *aSubject, const char *aTopic, const char16_t *data) |
|
196 { |
|
197 MOZ_ASSERT(!nsCRT::strcmp(aTopic, "webapps-clear-data")); |
|
198 |
|
199 nsCOMPtr<mozIApplicationClearPrivateDataParams> params = |
|
200 do_QueryInterface(aSubject); |
|
201 if (!params) { |
|
202 NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); |
|
203 return NS_ERROR_UNEXPECTED; |
|
204 } |
|
205 |
|
206 uint32_t appId; |
|
207 nsresult rv = params->GetAppId(&appId); |
|
208 NS_ENSURE_SUCCESS(rv, rv); |
|
209 |
|
210 bool browserOnly; |
|
211 rv = params->GetBrowserOnly(&browserOnly); |
|
212 NS_ENSURE_SUCCESS(rv, rv); |
|
213 |
|
214 nsCOMPtr<nsIPermissionManager> permManager = do_GetService("@mozilla.org/permissionmanager;1"); |
|
215 return permManager->RemovePermissionsForApp(appId, browserOnly); |
|
216 } |
|
217 }; |
|
218 |
|
219 NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver) |
|
220 |
|
221 static bool |
|
222 IsExpandedPrincipal(nsIPrincipal* aPrincipal) |
|
223 { |
|
224 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal); |
|
225 return !!ep; |
|
226 } |
|
227 |
|
228 } // anonymous namespace |
|
229 |
|
230 //////////////////////////////////////////////////////////////////////////////// |
|
231 |
|
232 nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal) |
|
233 { |
|
234 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost))); |
|
235 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId))); |
|
236 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement))); |
|
237 } |
|
238 |
|
239 /** |
|
240 * Simple callback used by |AsyncClose| to trigger a treatment once |
|
241 * the database is closed. |
|
242 * |
|
243 * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a |
|
244 * |nsPermissionManager|, this will create a cycle. |
|
245 * |
|
246 * Note: Once the callback has been called this DeleteFromMozHostListener cannot |
|
247 * be reused. |
|
248 */ |
|
249 class CloseDatabaseListener MOZ_FINAL : public mozIStorageCompletionCallback |
|
250 { |
|
251 public: |
|
252 NS_DECL_ISUPPORTS |
|
253 NS_DECL_MOZISTORAGECOMPLETIONCALLBACK |
|
254 /** |
|
255 * @param aManager The owning manager. |
|
256 * @param aRebuildOnSuccess If |true|, reinitialize the database once |
|
257 * it has been closed. Otherwise, do nothing such. |
|
258 */ |
|
259 CloseDatabaseListener(nsPermissionManager* aManager, |
|
260 bool aRebuildOnSuccess); |
|
261 |
|
262 protected: |
|
263 nsRefPtr<nsPermissionManager> mManager; |
|
264 bool mRebuildOnSuccess; |
|
265 }; |
|
266 |
|
267 NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback) |
|
268 |
|
269 CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager, |
|
270 bool aRebuildOnSuccess) |
|
271 : mManager(aManager) |
|
272 , mRebuildOnSuccess(aRebuildOnSuccess) |
|
273 { |
|
274 } |
|
275 |
|
276 NS_IMETHODIMP |
|
277 CloseDatabaseListener::Complete(nsresult, nsISupports*) |
|
278 { |
|
279 // Help breaking cycles |
|
280 nsRefPtr<nsPermissionManager> manager = mManager.forget(); |
|
281 if (mRebuildOnSuccess && !manager->mIsShuttingDown) { |
|
282 return manager->InitDB(true); |
|
283 } |
|
284 return NS_OK; |
|
285 } |
|
286 |
|
287 |
|
288 /** |
|
289 * Simple callback used by |RemoveAllInternal| to trigger closing |
|
290 * the database and reinitializing it. |
|
291 * |
|
292 * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a |
|
293 * |nsPermissionManager|, this will create a cycle. |
|
294 * |
|
295 * Note: Once the callback has been called this DeleteFromMozHostListener cannot |
|
296 * be reused. |
|
297 */ |
|
298 class DeleteFromMozHostListener MOZ_FINAL : public mozIStorageStatementCallback |
|
299 { |
|
300 public: |
|
301 NS_DECL_ISUPPORTS |
|
302 NS_DECL_MOZISTORAGESTATEMENTCALLBACK |
|
303 |
|
304 /** |
|
305 * @param aManager The owning manager. |
|
306 */ |
|
307 DeleteFromMozHostListener(nsPermissionManager* aManager); |
|
308 |
|
309 protected: |
|
310 nsRefPtr<nsPermissionManager> mManager; |
|
311 }; |
|
312 |
|
313 NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback) |
|
314 |
|
315 DeleteFromMozHostListener:: |
|
316 DeleteFromMozHostListener(nsPermissionManager* aManager) |
|
317 : mManager(aManager) |
|
318 { |
|
319 } |
|
320 |
|
321 NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *) |
|
322 { |
|
323 MOZ_CRASH("Should not get any results"); |
|
324 } |
|
325 |
|
326 NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *) |
|
327 { |
|
328 // Errors are handled in |HandleCompletion| |
|
329 return NS_OK; |
|
330 } |
|
331 |
|
332 NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason) |
|
333 { |
|
334 // Help breaking cycles |
|
335 nsRefPtr<nsPermissionManager> manager = mManager.forget(); |
|
336 |
|
337 if (aReason == REASON_ERROR) { |
|
338 manager->CloseDB(true); |
|
339 } |
|
340 |
|
341 return NS_OK; |
|
342 } |
|
343 |
|
344 /* static */ void |
|
345 nsPermissionManager::AppClearDataObserverInit() |
|
346 { |
|
347 nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1"); |
|
348 observerService->AddObserver(new AppClearDataObserver(), "webapps-clear-data", /* holdsWeak= */ false); |
|
349 } |
|
350 |
|
351 //////////////////////////////////////////////////////////////////////////////// |
|
352 // nsPermissionManager Implementation |
|
353 |
|
354 static const char kPermissionsFileName[] = "permissions.sqlite"; |
|
355 #define HOSTS_SCHEMA_VERSION 3 |
|
356 |
|
357 static const char kHostpermFileName[] = "hostperm.1"; |
|
358 |
|
359 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION; |
|
360 |
|
361 NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference) |
|
362 |
|
363 nsPermissionManager::nsPermissionManager() |
|
364 : mLargestID(0) |
|
365 , mIsShuttingDown(false) |
|
366 { |
|
367 } |
|
368 |
|
369 nsPermissionManager::~nsPermissionManager() |
|
370 { |
|
371 RemoveAllFromMemory(); |
|
372 gPermissionManager = nullptr; |
|
373 } |
|
374 |
|
375 // static |
|
376 nsIPermissionManager* |
|
377 nsPermissionManager::GetXPCOMSingleton() |
|
378 { |
|
379 if (gPermissionManager) { |
|
380 NS_ADDREF(gPermissionManager); |
|
381 return gPermissionManager; |
|
382 } |
|
383 |
|
384 // Create a new singleton nsPermissionManager. |
|
385 // We AddRef only once since XPCOM has rules about the ordering of module |
|
386 // teardowns - by the time our module destructor is called, it's too late to |
|
387 // Release our members, since GC cycles have already been completed and |
|
388 // would result in serious leaks. |
|
389 // See bug 209571. |
|
390 gPermissionManager = new nsPermissionManager(); |
|
391 if (gPermissionManager) { |
|
392 NS_ADDREF(gPermissionManager); |
|
393 if (NS_FAILED(gPermissionManager->Init())) { |
|
394 NS_RELEASE(gPermissionManager); |
|
395 } |
|
396 } |
|
397 |
|
398 return gPermissionManager; |
|
399 } |
|
400 |
|
401 nsresult |
|
402 nsPermissionManager::Init() |
|
403 { |
|
404 nsresult rv; |
|
405 |
|
406 mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv); |
|
407 if (NS_SUCCEEDED(rv)) { |
|
408 mObserverService->AddObserver(this, "profile-before-change", true); |
|
409 mObserverService->AddObserver(this, "profile-do-change", true); |
|
410 } |
|
411 |
|
412 |
|
413 nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
414 if (pbi) { |
|
415 pbi->AddObserver("permissions.", this, PR_FALSE); |
|
416 } |
|
417 |
|
418 if (IsChildProcess()) { |
|
419 // Get the permissions from the parent process |
|
420 InfallibleTArray<IPC::Permission> perms; |
|
421 ChildProcess()->SendReadPermissions(&perms); |
|
422 |
|
423 for (uint32_t i = 0; i < perms.Length(); i++) { |
|
424 const IPC::Permission &perm = perms[i]; |
|
425 |
|
426 nsCOMPtr<nsIPrincipal> principal; |
|
427 rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal)); |
|
428 NS_ENSURE_SUCCESS(rv, rv); |
|
429 |
|
430 AddInternal(principal, perm.type, perm.capability, 0, perm.expireType, |
|
431 perm.expireTime, eNotify, eNoDBOperation); |
|
432 } |
|
433 |
|
434 // Stop here; we don't need the DB in the child process |
|
435 return NS_OK; |
|
436 } |
|
437 |
|
438 // ignore failure here, since it's non-fatal (we can run fine without |
|
439 // persistent storage - e.g. if there's no profile). |
|
440 // XXX should we tell the user about this? |
|
441 InitDB(false); |
|
442 |
|
443 return NS_OK; |
|
444 } |
|
445 |
|
446 nsresult |
|
447 nsPermissionManager::InitDB(bool aRemoveFile) |
|
448 { |
|
449 nsCOMPtr<nsIFile> permissionsFile; |
|
450 nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile)); |
|
451 if (NS_FAILED(rv)) { |
|
452 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile)); |
|
453 } |
|
454 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); |
|
455 |
|
456 rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName)); |
|
457 NS_ENSURE_SUCCESS(rv, rv); |
|
458 |
|
459 if (aRemoveFile) { |
|
460 bool exists = false; |
|
461 rv = permissionsFile->Exists(&exists); |
|
462 NS_ENSURE_SUCCESS(rv, rv); |
|
463 if (exists) { |
|
464 rv = permissionsFile->Remove(false); |
|
465 NS_ENSURE_SUCCESS(rv, rv); |
|
466 } |
|
467 } |
|
468 |
|
469 nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); |
|
470 if (!storage) |
|
471 return NS_ERROR_UNEXPECTED; |
|
472 |
|
473 bool memory_db = false; |
|
474 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
|
475 if (prefs) { |
|
476 prefs->GetBoolPref("permissions.memory_only", &memory_db); |
|
477 } |
|
478 |
|
479 // cache a connection to the hosts database |
|
480 if (memory_db) { |
|
481 rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn)); |
|
482 } else { |
|
483 rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); |
|
484 } |
|
485 NS_ENSURE_SUCCESS(rv, rv); |
|
486 |
|
487 bool ready; |
|
488 mDBConn->GetConnectionReady(&ready); |
|
489 if (!ready) { |
|
490 // delete and try again |
|
491 rv = permissionsFile->Remove(false); |
|
492 NS_ENSURE_SUCCESS(rv, rv); |
|
493 |
|
494 if (memory_db) { |
|
495 rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn)); |
|
496 } else { |
|
497 rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); |
|
498 } |
|
499 NS_ENSURE_SUCCESS(rv, rv); |
|
500 |
|
501 mDBConn->GetConnectionReady(&ready); |
|
502 if (!ready) |
|
503 return NS_ERROR_UNEXPECTED; |
|
504 } |
|
505 |
|
506 bool tableExists = false; |
|
507 mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists); |
|
508 if (!tableExists) { |
|
509 rv = CreateTable(); |
|
510 NS_ENSURE_SUCCESS(rv, rv); |
|
511 |
|
512 } else { |
|
513 // table already exists; check the schema version before reading |
|
514 int32_t dbSchemaVersion; |
|
515 rv = mDBConn->GetSchemaVersion(&dbSchemaVersion); |
|
516 NS_ENSURE_SUCCESS(rv, rv); |
|
517 |
|
518 switch (dbSchemaVersion) { |
|
519 // upgrading. |
|
520 // every time you increment the database schema, you need to implement |
|
521 // the upgrading code from the previous version to the new one. |
|
522 // fall through to current version |
|
523 |
|
524 case 1: |
|
525 { |
|
526 // previous non-expiry version of database. Upgrade it by adding the |
|
527 // expiration columns |
|
528 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
529 "ALTER TABLE moz_hosts ADD expireType INTEGER")); |
|
530 NS_ENSURE_SUCCESS(rv, rv); |
|
531 |
|
532 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
533 "ALTER TABLE moz_hosts ADD expireTime INTEGER")); |
|
534 NS_ENSURE_SUCCESS(rv, rv); |
|
535 } |
|
536 |
|
537 // fall through to the next upgrade |
|
538 |
|
539 // TODO: we want to make default version as version 2 in order to fix bug 784875. |
|
540 case 0: |
|
541 case 2: |
|
542 { |
|
543 // Add appId/isInBrowserElement fields. |
|
544 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
545 "ALTER TABLE moz_hosts ADD appId INTEGER")); |
|
546 NS_ENSURE_SUCCESS(rv, rv); |
|
547 |
|
548 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
549 "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER")); |
|
550 NS_ENSURE_SUCCESS(rv, rv); |
|
551 |
|
552 rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); |
|
553 NS_ENSURE_SUCCESS(rv, rv); |
|
554 } |
|
555 |
|
556 // fall through to the next upgrade |
|
557 |
|
558 // current version. |
|
559 case HOSTS_SCHEMA_VERSION: |
|
560 break; |
|
561 |
|
562 // downgrading. |
|
563 // if columns have been added to the table, we can still use the ones we |
|
564 // understand safely. if columns have been deleted or altered, just |
|
565 // blow away the table and start from scratch! if you change the way |
|
566 // a column is interpreted, make sure you also change its name so this |
|
567 // check will catch it. |
|
568 default: |
|
569 { |
|
570 // check if all the expected columns exist |
|
571 nsCOMPtr<mozIStorageStatement> stmt; |
|
572 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( |
|
573 "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"), |
|
574 getter_AddRefs(stmt)); |
|
575 if (NS_SUCCEEDED(rv)) |
|
576 break; |
|
577 |
|
578 // our columns aren't there - drop the table! |
|
579 rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts")); |
|
580 NS_ENSURE_SUCCESS(rv, rv); |
|
581 |
|
582 rv = CreateTable(); |
|
583 NS_ENSURE_SUCCESS(rv, rv); |
|
584 } |
|
585 break; |
|
586 } |
|
587 } |
|
588 |
|
589 // make operations on the table asynchronous, for performance |
|
590 mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF")); |
|
591 |
|
592 // cache frequently used statements (for insertion, deletion, and updating) |
|
593 rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( |
|
594 "INSERT INTO moz_hosts " |
|
595 "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " |
|
596 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert)); |
|
597 NS_ENSURE_SUCCESS(rv, rv); |
|
598 |
|
599 rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( |
|
600 "DELETE FROM moz_hosts " |
|
601 "WHERE id = ?1"), getter_AddRefs(mStmtDelete)); |
|
602 NS_ENSURE_SUCCESS(rv, rv); |
|
603 |
|
604 rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( |
|
605 "UPDATE moz_hosts " |
|
606 "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"), |
|
607 getter_AddRefs(mStmtUpdate)); |
|
608 NS_ENSURE_SUCCESS(rv, rv); |
|
609 |
|
610 // check whether to import or just read in the db |
|
611 if (tableExists) |
|
612 return Read(); |
|
613 |
|
614 return Import(); |
|
615 } |
|
616 |
|
617 // sets the schema version and creates the moz_hosts table. |
|
618 nsresult |
|
619 nsPermissionManager::CreateTable() |
|
620 { |
|
621 // set the schema version, before creating the table |
|
622 nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); |
|
623 if (NS_FAILED(rv)) return rv; |
|
624 |
|
625 // create the table |
|
626 // SQL also lives in automation.py.in. If you change this SQL change that |
|
627 // one too. |
|
628 return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
|
629 "CREATE TABLE moz_hosts (" |
|
630 " id INTEGER PRIMARY KEY" |
|
631 ",host TEXT" |
|
632 ",type TEXT" |
|
633 ",permission INTEGER" |
|
634 ",expireType INTEGER" |
|
635 ",expireTime INTEGER" |
|
636 ",appId INTEGER" |
|
637 ",isInBrowserElement INTEGER" |
|
638 ")")); |
|
639 } |
|
640 |
|
641 NS_IMETHODIMP |
|
642 nsPermissionManager::Add(nsIURI *aURI, |
|
643 const char *aType, |
|
644 uint32_t aPermission, |
|
645 uint32_t aExpireType, |
|
646 int64_t aExpireTime) |
|
647 { |
|
648 NS_ENSURE_ARG_POINTER(aURI); |
|
649 |
|
650 nsCOMPtr<nsIPrincipal> principal; |
|
651 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); |
|
652 NS_ENSURE_SUCCESS(rv, rv); |
|
653 |
|
654 return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime); |
|
655 } |
|
656 |
|
657 NS_IMETHODIMP |
|
658 nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal, |
|
659 const char* aType, uint32_t aPermission, |
|
660 uint32_t aExpireType, int64_t aExpireTime) |
|
661 { |
|
662 ENSURE_NOT_CHILD_PROCESS; |
|
663 NS_ENSURE_ARG_POINTER(aPrincipal); |
|
664 NS_ENSURE_ARG_POINTER(aType); |
|
665 NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER || |
|
666 aExpireType == nsIPermissionManager::EXPIRE_TIME || |
|
667 aExpireType == nsIPermissionManager::EXPIRE_SESSION, |
|
668 NS_ERROR_INVALID_ARG); |
|
669 |
|
670 // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only |
|
671 // honors expireTime if it is nonzero. |
|
672 if ((aExpireType == nsIPermissionManager::EXPIRE_TIME || |
|
673 (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) && |
|
674 aExpireTime <= (PR_Now() / 1000)) { |
|
675 return NS_OK; |
|
676 } |
|
677 |
|
678 // We don't add the system principal because it actually has no URI and we |
|
679 // always allow action for them. |
|
680 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
|
681 return NS_OK; |
|
682 } |
|
683 |
|
684 // Permissions may not be added to expanded principals. |
|
685 if (IsExpandedPrincipal(aPrincipal)) { |
|
686 return NS_ERROR_INVALID_ARG; |
|
687 } |
|
688 |
|
689 return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0, |
|
690 aExpireType, aExpireTime, eNotify, eWriteToDB); |
|
691 } |
|
692 |
|
693 nsresult |
|
694 nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, |
|
695 const nsAFlatCString &aType, |
|
696 uint32_t aPermission, |
|
697 int64_t aID, |
|
698 uint32_t aExpireType, |
|
699 int64_t aExpireTime, |
|
700 NotifyOperationType aNotifyOperation, |
|
701 DBOperationType aDBOperation) |
|
702 { |
|
703 nsAutoCString host; |
|
704 nsresult rv = GetHostForPrincipal(aPrincipal, host); |
|
705 NS_ENSURE_SUCCESS(rv, rv); |
|
706 |
|
707 if (!IsChildProcess()) { |
|
708 uint32_t appId; |
|
709 rv = aPrincipal->GetAppId(&appId); |
|
710 NS_ENSURE_SUCCESS(rv, rv); |
|
711 |
|
712 bool isInBrowserElement; |
|
713 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
|
714 NS_ENSURE_SUCCESS(rv, rv); |
|
715 |
|
716 IPC::Permission permission(host, appId, isInBrowserElement, aType, |
|
717 aPermission, aExpireType, aExpireTime); |
|
718 |
|
719 nsTArray<ContentParent*> cplist; |
|
720 ContentParent::GetAll(cplist); |
|
721 for (uint32_t i = 0; i < cplist.Length(); ++i) { |
|
722 ContentParent* cp = cplist[i]; |
|
723 if (cp->NeedsPermissionsUpdate()) |
|
724 unused << cp->SendAddPermission(permission); |
|
725 } |
|
726 } |
|
727 |
|
728 // look up the type index |
|
729 int32_t typeIndex = GetTypeIndex(aType.get(), true); |
|
730 NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY); |
|
731 |
|
732 // When an entry already exists, PutEntry will return that, instead |
|
733 // of adding a new one |
|
734 nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal); |
|
735 PermissionHashKey* entry = mPermissionTable.PutEntry(key); |
|
736 if (!entry) return NS_ERROR_FAILURE; |
|
737 if (!entry->GetKey()) { |
|
738 mPermissionTable.RawRemoveEntry(entry); |
|
739 return NS_ERROR_OUT_OF_MEMORY; |
|
740 } |
|
741 |
|
742 // figure out the transaction type, and get any existing permission value |
|
743 OperationType op; |
|
744 int32_t index = entry->GetPermissionIndex(typeIndex); |
|
745 if (index == -1) { |
|
746 if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) |
|
747 op = eOperationNone; |
|
748 else |
|
749 op = eOperationAdding; |
|
750 |
|
751 } else { |
|
752 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; |
|
753 |
|
754 // remove the permission if the permission is UNKNOWN, update the |
|
755 // permission if its value or expire type have changed OR if the time has |
|
756 // changed and the expire type is time, otherwise, don't modify. There's |
|
757 // no need to modify a permission that doesn't expire with time when the |
|
758 // only thing changed is the expire time. |
|
759 if (aPermission == oldPermissionEntry.mPermission && |
|
760 aExpireType == oldPermissionEntry.mExpireType && |
|
761 (aExpireType == nsIPermissionManager::EXPIRE_NEVER || |
|
762 aExpireTime == oldPermissionEntry.mExpireTime)) |
|
763 op = eOperationNone; |
|
764 else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) |
|
765 op = eOperationRemoving; |
|
766 else |
|
767 op = eOperationChanging; |
|
768 } |
|
769 |
|
770 // do the work for adding, deleting, or changing a permission: |
|
771 // update the in-memory list, write to the db, and notify consumers. |
|
772 int64_t id; |
|
773 switch (op) { |
|
774 case eOperationNone: |
|
775 { |
|
776 // nothing to do |
|
777 return NS_OK; |
|
778 } |
|
779 |
|
780 case eOperationAdding: |
|
781 { |
|
782 if (aDBOperation == eWriteToDB) { |
|
783 // we'll be writing to the database - generate a known unique id |
|
784 id = ++mLargestID; |
|
785 } else { |
|
786 // we're reading from the database - use the id already assigned |
|
787 id = aID; |
|
788 } |
|
789 |
|
790 entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime)); |
|
791 |
|
792 if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) { |
|
793 uint32_t appId; |
|
794 rv = aPrincipal->GetAppId(&appId); |
|
795 NS_ENSURE_SUCCESS(rv, rv); |
|
796 |
|
797 bool isInBrowserElement; |
|
798 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
|
799 NS_ENSURE_SUCCESS(rv, rv); |
|
800 |
|
801 UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); |
|
802 } |
|
803 |
|
804 if (aNotifyOperation == eNotify) { |
|
805 NotifyObserversWithPermission(host, |
|
806 entry->GetKey()->mAppId, |
|
807 entry->GetKey()->mIsInBrowserElement, |
|
808 mTypeArray[typeIndex], |
|
809 aPermission, |
|
810 aExpireType, |
|
811 aExpireTime, |
|
812 MOZ_UTF16("added")); |
|
813 } |
|
814 |
|
815 break; |
|
816 } |
|
817 |
|
818 case eOperationRemoving: |
|
819 { |
|
820 PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; |
|
821 id = oldPermissionEntry.mID; |
|
822 entry->GetPermissions().RemoveElementAt(index); |
|
823 |
|
824 if (aDBOperation == eWriteToDB) |
|
825 // We care only about the id here so we pass dummy values for all other |
|
826 // parameters. |
|
827 UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, |
|
828 nsIPermissionManager::EXPIRE_NEVER, 0, 0, false); |
|
829 |
|
830 if (aNotifyOperation == eNotify) { |
|
831 NotifyObserversWithPermission(host, |
|
832 entry->GetKey()->mAppId, |
|
833 entry->GetKey()->mIsInBrowserElement, |
|
834 mTypeArray[typeIndex], |
|
835 oldPermissionEntry.mPermission, |
|
836 oldPermissionEntry.mExpireType, |
|
837 oldPermissionEntry.mExpireTime, |
|
838 MOZ_UTF16("deleted")); |
|
839 } |
|
840 |
|
841 // If there are no more permissions stored for that entry, clear it. |
|
842 if (entry->GetPermissions().IsEmpty()) { |
|
843 mPermissionTable.RawRemoveEntry(entry); |
|
844 } |
|
845 |
|
846 break; |
|
847 } |
|
848 |
|
849 case eOperationChanging: |
|
850 { |
|
851 id = entry->GetPermissions()[index].mID; |
|
852 |
|
853 // If the new expireType is EXPIRE_SESSION, then we have to keep a |
|
854 // copy of the previous permission/expireType values. This cached value will be |
|
855 // used when restoring the permissions of an app. |
|
856 if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION && |
|
857 aExpireType == nsIPermissionManager::EXPIRE_SESSION) { |
|
858 entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission; |
|
859 entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType; |
|
860 entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime; |
|
861 } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) { |
|
862 entry->GetPermissions()[index].mNonSessionPermission = aPermission; |
|
863 entry->GetPermissions()[index].mNonSessionExpireType = aExpireType; |
|
864 entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime; |
|
865 } |
|
866 |
|
867 entry->GetPermissions()[index].mPermission = aPermission; |
|
868 entry->GetPermissions()[index].mExpireType = aExpireType; |
|
869 entry->GetPermissions()[index].mExpireTime = aExpireTime; |
|
870 |
|
871 if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) |
|
872 // We care only about the id, the permission and expireType/expireTime here. |
|
873 // We pass dummy values for all other parameters. |
|
874 UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), |
|
875 aPermission, aExpireType, aExpireTime, 0, false); |
|
876 |
|
877 if (aNotifyOperation == eNotify) { |
|
878 NotifyObserversWithPermission(host, |
|
879 entry->GetKey()->mAppId, |
|
880 entry->GetKey()->mIsInBrowserElement, |
|
881 mTypeArray[typeIndex], |
|
882 aPermission, |
|
883 aExpireType, |
|
884 aExpireTime, |
|
885 MOZ_UTF16("changed")); |
|
886 } |
|
887 |
|
888 break; |
|
889 } |
|
890 } |
|
891 |
|
892 return NS_OK; |
|
893 } |
|
894 |
|
895 NS_IMETHODIMP |
|
896 nsPermissionManager::Remove(const nsACString &aHost, |
|
897 const char *aType) |
|
898 { |
|
899 nsCOMPtr<nsIPrincipal> principal; |
|
900 nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal)); |
|
901 NS_ENSURE_SUCCESS(rv, rv); |
|
902 |
|
903 return RemoveFromPrincipal(principal, aType); |
|
904 } |
|
905 |
|
906 NS_IMETHODIMP |
|
907 nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal, |
|
908 const char* aType) |
|
909 { |
|
910 ENSURE_NOT_CHILD_PROCESS; |
|
911 NS_ENSURE_ARG_POINTER(aPrincipal); |
|
912 NS_ENSURE_ARG_POINTER(aType); |
|
913 |
|
914 // System principals are never added to the database, no need to remove them. |
|
915 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
|
916 return NS_OK; |
|
917 } |
|
918 |
|
919 // Permissions may not be added to expanded principals. |
|
920 if (IsExpandedPrincipal(aPrincipal)) { |
|
921 return NS_ERROR_INVALID_ARG; |
|
922 } |
|
923 |
|
924 // AddInternal() handles removal, just let it do the work |
|
925 return AddInternal(aPrincipal, |
|
926 nsDependentCString(aType), |
|
927 nsIPermissionManager::UNKNOWN_ACTION, |
|
928 0, |
|
929 nsIPermissionManager::EXPIRE_NEVER, |
|
930 0, |
|
931 eNotify, |
|
932 eWriteToDB); |
|
933 } |
|
934 |
|
935 NS_IMETHODIMP |
|
936 nsPermissionManager::RemoveAll() |
|
937 { |
|
938 ENSURE_NOT_CHILD_PROCESS; |
|
939 return RemoveAllInternal(true); |
|
940 } |
|
941 |
|
942 void |
|
943 nsPermissionManager::CloseDB(bool aRebuildOnSuccess) |
|
944 { |
|
945 // Null the statements, this will finalize them. |
|
946 mStmtInsert = nullptr; |
|
947 mStmtDelete = nullptr; |
|
948 mStmtUpdate = nullptr; |
|
949 if (mDBConn) { |
|
950 mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this, |
|
951 aRebuildOnSuccess); |
|
952 mozilla::DebugOnly<nsresult> rv = mDBConn->AsyncClose(cb); |
|
953 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
954 mDBConn = nullptr; // Avoid race conditions |
|
955 } |
|
956 } |
|
957 |
|
958 nsresult |
|
959 nsPermissionManager::RemoveAllInternal(bool aNotifyObservers) |
|
960 { |
|
961 // Remove from memory and notify immediately. Since the in-memory |
|
962 // database is authoritative, we do not need confirmation from the |
|
963 // on-disk database to notify observers. |
|
964 RemoveAllFromMemory(); |
|
965 if (aNotifyObservers) { |
|
966 NotifyObservers(nullptr, MOZ_UTF16("cleared")); |
|
967 } |
|
968 |
|
969 // clear the db |
|
970 if (mDBConn) { |
|
971 nsCOMPtr<mozIStorageAsyncStatement> removeStmt; |
|
972 nsresult rv = mDBConn-> |
|
973 CreateAsyncStatement(NS_LITERAL_CSTRING( |
|
974 "DELETE FROM moz_hosts" |
|
975 ), getter_AddRefs(removeStmt)); |
|
976 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
977 if (!removeStmt) { |
|
978 return NS_ERROR_UNEXPECTED; |
|
979 } |
|
980 nsCOMPtr<mozIStoragePendingStatement> pending; |
|
981 mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this); |
|
982 rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending)); |
|
983 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
984 |
|
985 return rv; |
|
986 } |
|
987 |
|
988 return NS_OK; |
|
989 } |
|
990 |
|
991 NS_IMETHODIMP |
|
992 nsPermissionManager::TestExactPermission(nsIURI *aURI, |
|
993 const char *aType, |
|
994 uint32_t *aPermission) |
|
995 { |
|
996 nsCOMPtr<nsIPrincipal> principal; |
|
997 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); |
|
998 NS_ENSURE_SUCCESS(rv, rv); |
|
999 |
|
1000 return TestExactPermissionFromPrincipal(principal, aType, aPermission); |
|
1001 } |
|
1002 |
|
1003 NS_IMETHODIMP |
|
1004 nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal, |
|
1005 const char* aType, |
|
1006 uint32_t* aPermission) |
|
1007 { |
|
1008 return CommonTestPermission(aPrincipal, aType, aPermission, true, true); |
|
1009 } |
|
1010 |
|
1011 NS_IMETHODIMP |
|
1012 nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal, |
|
1013 const char* aType, |
|
1014 uint32_t* aPermission) |
|
1015 { |
|
1016 return CommonTestPermission(aPrincipal, aType, aPermission, true, false); |
|
1017 } |
|
1018 |
|
1019 NS_IMETHODIMP |
|
1020 nsPermissionManager::TestPermission(nsIURI *aURI, |
|
1021 const char *aType, |
|
1022 uint32_t *aPermission) |
|
1023 { |
|
1024 nsCOMPtr<nsIPrincipal> principal; |
|
1025 nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); |
|
1026 NS_ENSURE_SUCCESS(rv, rv); |
|
1027 |
|
1028 return TestPermissionFromPrincipal(principal, aType, aPermission); |
|
1029 } |
|
1030 |
|
1031 NS_IMETHODIMP |
|
1032 nsPermissionManager::TestPermissionFromWindow(nsIDOMWindow* aWindow, |
|
1033 const char* aType, |
|
1034 uint32_t* aPermission) |
|
1035 { |
|
1036 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow); |
|
1037 NS_ENSURE_TRUE(window, NS_NOINTERFACE); |
|
1038 |
|
1039 nsPIDOMWindow* innerWindow = window->IsInnerWindow() ? |
|
1040 window.get() : |
|
1041 window->GetCurrentInnerWindow(); |
|
1042 |
|
1043 // Get the document for security check |
|
1044 nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc(); |
|
1045 NS_ENSURE_TRUE(document, NS_NOINTERFACE); |
|
1046 |
|
1047 nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal(); |
|
1048 return TestPermissionFromPrincipal(principal, aType, aPermission); |
|
1049 } |
|
1050 |
|
1051 NS_IMETHODIMP |
|
1052 nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal, |
|
1053 const char* aType, |
|
1054 uint32_t* aPermission) |
|
1055 { |
|
1056 return CommonTestPermission(aPrincipal, aType, aPermission, false, true); |
|
1057 } |
|
1058 |
|
1059 NS_IMETHODIMP |
|
1060 nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal, |
|
1061 const char* aType, |
|
1062 bool aExactHostMatch, |
|
1063 nsIPermission** aResult) |
|
1064 { |
|
1065 NS_ENSURE_ARG_POINTER(aPrincipal); |
|
1066 NS_ENSURE_ARG_POINTER(aType); |
|
1067 |
|
1068 *aResult = nullptr; |
|
1069 |
|
1070 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
|
1071 return NS_OK; |
|
1072 } |
|
1073 |
|
1074 // Querying the permission object of an nsEP is non-sensical. |
|
1075 if (IsExpandedPrincipal(aPrincipal)) { |
|
1076 return NS_ERROR_INVALID_ARG; |
|
1077 } |
|
1078 |
|
1079 nsAutoCString host; |
|
1080 nsresult rv = GetHostForPrincipal(aPrincipal, host); |
|
1081 NS_ENSURE_SUCCESS(rv, rv); |
|
1082 |
|
1083 int32_t typeIndex = GetTypeIndex(aType, false); |
|
1084 // If type == -1, the type isn't known, |
|
1085 // so just return NS_OK |
|
1086 if (typeIndex == -1) return NS_OK; |
|
1087 |
|
1088 uint32_t appId; |
|
1089 rv = aPrincipal->GetAppId(&appId); |
|
1090 NS_ENSURE_SUCCESS(rv, rv); |
|
1091 |
|
1092 bool isInBrowserElement; |
|
1093 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
|
1094 NS_ENSURE_SUCCESS(rv, rv); |
|
1095 |
|
1096 PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, |
|
1097 typeIndex, aExactHostMatch); |
|
1098 if (!entry) { |
|
1099 return NS_OK; |
|
1100 } |
|
1101 |
|
1102 // We don't call GetPermission(typeIndex) because that returns a fake |
|
1103 // UNKNOWN_ACTION entry if there is no match. |
|
1104 int32_t idx = entry->GetPermissionIndex(typeIndex); |
|
1105 if (-1 == idx) { |
|
1106 return NS_OK; |
|
1107 } |
|
1108 |
|
1109 PermissionEntry& perm = entry->GetPermissions()[idx]; |
|
1110 nsCOMPtr<nsIPermission> r = new nsPermission(entry->GetKey()->mHost, |
|
1111 entry->GetKey()->mAppId, |
|
1112 entry->GetKey()->mIsInBrowserElement, |
|
1113 mTypeArray.ElementAt(perm.mType), |
|
1114 perm.mPermission, |
|
1115 perm.mExpireType, |
|
1116 perm.mExpireTime); |
|
1117 r.forget(aResult); |
|
1118 return NS_OK; |
|
1119 } |
|
1120 |
|
1121 nsresult |
|
1122 nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, |
|
1123 const char *aType, |
|
1124 uint32_t *aPermission, |
|
1125 bool aExactHostMatch, |
|
1126 bool aIncludingSession) |
|
1127 { |
|
1128 NS_ENSURE_ARG_POINTER(aPrincipal); |
|
1129 NS_ENSURE_ARG_POINTER(aType); |
|
1130 |
|
1131 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
|
1132 *aPermission = nsIPermissionManager::ALLOW_ACTION; |
|
1133 return NS_OK; |
|
1134 } |
|
1135 |
|
1136 // Set the default. |
|
1137 *aPermission = nsIPermissionManager::UNKNOWN_ACTION; |
|
1138 |
|
1139 // For expanded principals, we want to iterate over the whitelist and see |
|
1140 // if the permission is granted for any of them. |
|
1141 nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal); |
|
1142 if (ep) { |
|
1143 nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist; |
|
1144 nsresult rv = ep->GetWhiteList(&whitelist); |
|
1145 NS_ENSURE_SUCCESS(rv, rv); |
|
1146 |
|
1147 for (size_t i = 0; i < whitelist->Length(); ++i) { |
|
1148 uint32_t perm; |
|
1149 rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch, |
|
1150 aIncludingSession); |
|
1151 NS_ENSURE_SUCCESS(rv, rv); |
|
1152 if (perm == nsIPermissionManager::ALLOW_ACTION) { |
|
1153 *aPermission = perm; |
|
1154 return NS_OK; |
|
1155 } else if (perm == nsIPermissionManager::PROMPT_ACTION) { |
|
1156 // Store it, but keep going to see if we can do better. |
|
1157 *aPermission = perm; |
|
1158 } |
|
1159 } |
|
1160 |
|
1161 return NS_OK; |
|
1162 } |
|
1163 |
|
1164 nsAutoCString host; |
|
1165 nsresult rv = GetHostForPrincipal(aPrincipal, host); |
|
1166 NS_ENSURE_SUCCESS(rv, rv); |
|
1167 |
|
1168 int32_t typeIndex = GetTypeIndex(aType, false); |
|
1169 // If type == -1, the type isn't known, |
|
1170 // so just return NS_OK |
|
1171 if (typeIndex == -1) return NS_OK; |
|
1172 |
|
1173 uint32_t appId; |
|
1174 rv = aPrincipal->GetAppId(&appId); |
|
1175 NS_ENSURE_SUCCESS(rv, rv); |
|
1176 |
|
1177 bool isInBrowserElement; |
|
1178 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
|
1179 NS_ENSURE_SUCCESS(rv, rv); |
|
1180 |
|
1181 PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, |
|
1182 typeIndex, aExactHostMatch); |
|
1183 if (!entry || |
|
1184 (!aIncludingSession && |
|
1185 entry->GetPermission(typeIndex).mNonSessionExpireType == |
|
1186 nsIPermissionManager::EXPIRE_SESSION)) { |
|
1187 return NS_OK; |
|
1188 } |
|
1189 |
|
1190 *aPermission = aIncludingSession |
|
1191 ? entry->GetPermission(typeIndex).mPermission |
|
1192 : entry->GetPermission(typeIndex).mNonSessionPermission; |
|
1193 |
|
1194 return NS_OK; |
|
1195 } |
|
1196 |
|
1197 // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple. |
|
1198 // This is not simply using PermissionKey because we will walk-up domains in |
|
1199 // case of |host| contains sub-domains. |
|
1200 // Returns null if nothing found. |
|
1201 // Also accepts host on the format "<foo>". This will perform an exact match |
|
1202 // lookup as the string doesn't contain any dots. |
|
1203 nsPermissionManager::PermissionHashKey* |
|
1204 nsPermissionManager::GetPermissionHashKey(const nsACString& aHost, |
|
1205 uint32_t aAppId, |
|
1206 bool aIsInBrowserElement, |
|
1207 uint32_t aType, |
|
1208 bool aExactHostMatch) |
|
1209 { |
|
1210 PermissionHashKey* entry = nullptr; |
|
1211 |
|
1212 nsRefPtr<PermissionKey> key = new PermissionKey(aHost, aAppId, aIsInBrowserElement); |
|
1213 entry = mPermissionTable.GetEntry(key); |
|
1214 |
|
1215 if (entry) { |
|
1216 PermissionEntry permEntry = entry->GetPermission(aType); |
|
1217 |
|
1218 // if the entry is expired, remove and keep looking for others. |
|
1219 // Note that EXPIRE_SESSION only honors expireTime if it is nonzero. |
|
1220 if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME || |
|
1221 (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION && |
|
1222 permEntry.mExpireTime != 0)) && |
|
1223 permEntry.mExpireTime <= (PR_Now() / 1000)) { |
|
1224 nsCOMPtr<nsIPrincipal> principal; |
|
1225 if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) { |
|
1226 return nullptr; |
|
1227 } |
|
1228 |
|
1229 entry = nullptr; |
|
1230 RemoveFromPrincipal(principal, mTypeArray[aType].get()); |
|
1231 } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { |
|
1232 entry = nullptr; |
|
1233 } |
|
1234 } |
|
1235 |
|
1236 if (entry) { |
|
1237 return entry; |
|
1238 } |
|
1239 |
|
1240 // If we haven't found an entry, depending on the host, we could try a bit |
|
1241 // harder. |
|
1242 // If this is a file:// URI, we can check for the presence of the magic entry |
|
1243 // <file> which gives permission to all file://. This hack might disappear, |
|
1244 // see bug 817007. Note that we don't require aExactHostMatch to be true for |
|
1245 // that to keep retro-compatibility. |
|
1246 // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can |
|
1247 // check if the base domain has a permission entry. |
|
1248 |
|
1249 if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) { |
|
1250 return GetPermissionHashKey(NS_LITERAL_CSTRING("<file>"), aAppId, aIsInBrowserElement, aType, true); |
|
1251 } |
|
1252 |
|
1253 if (!aExactHostMatch) { |
|
1254 nsCString domain = GetNextSubDomainForHost(aHost); |
|
1255 if (!domain.IsEmpty()) { |
|
1256 return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch); |
|
1257 } |
|
1258 } |
|
1259 |
|
1260 // No entry, really... |
|
1261 return nullptr; |
|
1262 } |
|
1263 |
|
1264 // helper struct for passing arguments into hash enumeration callback. |
|
1265 struct nsGetEnumeratorData |
|
1266 { |
|
1267 nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes) |
|
1268 : array(aArray) |
|
1269 , types(aTypes) {} |
|
1270 |
|
1271 nsCOMArray<nsIPermission> *array; |
|
1272 const nsTArray<nsCString> *types; |
|
1273 }; |
|
1274 |
|
1275 static PLDHashOperator |
|
1276 AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) |
|
1277 { |
|
1278 nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg); |
|
1279 |
|
1280 for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { |
|
1281 nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; |
|
1282 |
|
1283 nsPermission *perm = new nsPermission(entry->GetKey()->mHost, |
|
1284 entry->GetKey()->mAppId, |
|
1285 entry->GetKey()->mIsInBrowserElement, |
|
1286 data->types->ElementAt(permEntry.mType), |
|
1287 permEntry.mPermission, |
|
1288 permEntry.mExpireType, |
|
1289 permEntry.mExpireTime); |
|
1290 |
|
1291 data->array->AppendObject(perm); |
|
1292 } |
|
1293 |
|
1294 return PL_DHASH_NEXT; |
|
1295 } |
|
1296 |
|
1297 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum) |
|
1298 { |
|
1299 // roll an nsCOMArray of all our permissions, then hand out an enumerator |
|
1300 nsCOMArray<nsIPermission> array; |
|
1301 nsGetEnumeratorData data(&array, &mTypeArray); |
|
1302 |
|
1303 mPermissionTable.EnumerateEntries(AddPermissionsToList, &data); |
|
1304 |
|
1305 return NS_NewArrayEnumerator(aEnum, array); |
|
1306 } |
|
1307 |
|
1308 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) |
|
1309 { |
|
1310 ENSURE_NOT_CHILD_PROCESS; |
|
1311 |
|
1312 if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { |
|
1313 if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) { |
|
1314 // XXX: Should we remove the file? Probably not.. |
|
1315 InitDB(PR_FALSE); |
|
1316 } |
|
1317 } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) { |
|
1318 // The profile is about to change, |
|
1319 // or is going away because the application is shutting down. |
|
1320 mIsShuttingDown = true; |
|
1321 if (!nsCRT::strcmp(someData, MOZ_UTF16("shutdown-cleanse"))) { |
|
1322 // Clear the permissions file and close the db asynchronously |
|
1323 RemoveAllInternal(false); |
|
1324 } else { |
|
1325 RemoveAllFromMemory(); |
|
1326 CloseDB(false); |
|
1327 } |
|
1328 } |
|
1329 else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { |
|
1330 // the profile has already changed; init the db from the new location |
|
1331 InitDB(false); |
|
1332 } |
|
1333 |
|
1334 return NS_OK; |
|
1335 } |
|
1336 |
|
1337 PLDHashOperator |
|
1338 nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg) |
|
1339 { |
|
1340 GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg); |
|
1341 |
|
1342 for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { |
|
1343 nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; |
|
1344 |
|
1345 if (entry->GetKey()->mAppId != data->appId || |
|
1346 (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) { |
|
1347 continue; |
|
1348 } |
|
1349 |
|
1350 data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost, |
|
1351 entry->GetKey()->mAppId, |
|
1352 entry->GetKey()->mIsInBrowserElement, |
|
1353 gPermissionManager->mTypeArray.ElementAt(permEntry.mType), |
|
1354 permEntry.mPermission, |
|
1355 permEntry.mExpireType, |
|
1356 permEntry.mExpireTime)); |
|
1357 } |
|
1358 |
|
1359 return PL_DHASH_NEXT; |
|
1360 } |
|
1361 |
|
1362 NS_IMETHODIMP |
|
1363 nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) |
|
1364 { |
|
1365 ENSURE_NOT_CHILD_PROCESS; |
|
1366 NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID); |
|
1367 |
|
1368 // We begin by removing all the permissions from the DB. |
|
1369 // After clearing the DB, we call AddInternal() to make sure that all |
|
1370 // processes are aware of this change and the representation of the DB in |
|
1371 // memory is updated. |
|
1372 // We have to get all permissions associated with an application and then |
|
1373 // remove those because doing so in EnumerateEntries() would fail because |
|
1374 // we might happen to actually delete entries from the list. |
|
1375 |
|
1376 nsAutoCString sql; |
|
1377 sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId="); |
|
1378 sql.AppendInt(aAppId); |
|
1379 |
|
1380 if (aBrowserOnly) { |
|
1381 sql.AppendLiteral(" AND isInBrowserElement=1"); |
|
1382 } |
|
1383 |
|
1384 nsCOMPtr<mozIStorageAsyncStatement> removeStmt; |
|
1385 nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt)); |
|
1386 NS_ENSURE_SUCCESS(rv, rv); |
|
1387 |
|
1388 nsCOMPtr<mozIStoragePendingStatement> pending; |
|
1389 rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); |
|
1390 NS_ENSURE_SUCCESS(rv, rv); |
|
1391 |
|
1392 GetPermissionsForAppStruct data(aAppId, aBrowserOnly); |
|
1393 mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data); |
|
1394 |
|
1395 for (int32_t i=0; i<data.permissions.Count(); ++i) { |
|
1396 nsAutoCString host; |
|
1397 bool isInBrowserElement; |
|
1398 nsAutoCString type; |
|
1399 |
|
1400 data.permissions[i]->GetHost(host); |
|
1401 data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement); |
|
1402 data.permissions[i]->GetType(type); |
|
1403 |
|
1404 nsCOMPtr<nsIPrincipal> principal; |
|
1405 if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement, |
|
1406 getter_AddRefs(principal)))) { |
|
1407 NS_ERROR("GetPrincipal() failed!"); |
|
1408 continue; |
|
1409 } |
|
1410 |
|
1411 AddInternal(principal, |
|
1412 type, |
|
1413 nsIPermissionManager::UNKNOWN_ACTION, |
|
1414 0, |
|
1415 nsIPermissionManager::EXPIRE_NEVER, |
|
1416 0, |
|
1417 nsPermissionManager::eNotify, |
|
1418 nsPermissionManager::eNoDBOperation); |
|
1419 } |
|
1420 |
|
1421 return NS_OK; |
|
1422 } |
|
1423 |
|
1424 PLDHashOperator |
|
1425 nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( |
|
1426 nsPermissionManager::PermissionHashKey* entry, void* arg) |
|
1427 { |
|
1428 uint32_t* appId = static_cast<uint32_t*>(arg); |
|
1429 |
|
1430 for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { |
|
1431 if (entry->GetKey()->mAppId != *appId) { |
|
1432 continue; |
|
1433 } |
|
1434 |
|
1435 nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; |
|
1436 if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) { |
|
1437 continue; |
|
1438 } |
|
1439 |
|
1440 if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) { |
|
1441 PermissionEntry oldPermissionEntry = entry->GetPermissions()[i]; |
|
1442 |
|
1443 entry->GetPermissions().RemoveElementAt(i); |
|
1444 |
|
1445 gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, |
|
1446 entry->GetKey()->mAppId, |
|
1447 entry->GetKey()->mIsInBrowserElement, |
|
1448 gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType), |
|
1449 oldPermissionEntry.mPermission, |
|
1450 oldPermissionEntry.mExpireType, |
|
1451 oldPermissionEntry.mExpireTime, |
|
1452 MOZ_UTF16("deleted")); |
|
1453 --i; |
|
1454 continue; |
|
1455 } |
|
1456 |
|
1457 permEntry.mPermission = permEntry.mNonSessionPermission; |
|
1458 permEntry.mExpireType = permEntry.mNonSessionExpireType; |
|
1459 permEntry.mExpireTime = permEntry.mNonSessionExpireTime; |
|
1460 |
|
1461 gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, |
|
1462 entry->GetKey()->mAppId, |
|
1463 entry->GetKey()->mIsInBrowserElement, |
|
1464 gPermissionManager->mTypeArray.ElementAt(permEntry.mType), |
|
1465 permEntry.mPermission, |
|
1466 permEntry.mExpireType, |
|
1467 permEntry.mExpireTime, |
|
1468 MOZ_UTF16("changed")); |
|
1469 } |
|
1470 |
|
1471 return PL_DHASH_NEXT; |
|
1472 } |
|
1473 |
|
1474 nsresult |
|
1475 nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId) |
|
1476 { |
|
1477 ENSURE_NOT_CHILD_PROCESS; |
|
1478 |
|
1479 if (aAppId != nsIScriptSecurityManager::NO_APP_ID) { |
|
1480 mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId); |
|
1481 } |
|
1482 |
|
1483 return NS_OK; |
|
1484 } |
|
1485 |
|
1486 //***************************************************************************** |
|
1487 //*** nsPermissionManager private methods |
|
1488 //***************************************************************************** |
|
1489 |
|
1490 nsresult |
|
1491 nsPermissionManager::RemoveAllFromMemory() |
|
1492 { |
|
1493 mLargestID = 0; |
|
1494 mTypeArray.Clear(); |
|
1495 mPermissionTable.Clear(); |
|
1496 |
|
1497 return NS_OK; |
|
1498 } |
|
1499 |
|
1500 // Returns -1 on failure |
|
1501 int32_t |
|
1502 nsPermissionManager::GetTypeIndex(const char *aType, |
|
1503 bool aAdd) |
|
1504 { |
|
1505 for (uint32_t i = 0; i < mTypeArray.Length(); ++i) |
|
1506 if (mTypeArray[i].Equals(aType)) |
|
1507 return i; |
|
1508 |
|
1509 if (!aAdd) { |
|
1510 // Not found, but that is ok - we were just looking. |
|
1511 return -1; |
|
1512 } |
|
1513 |
|
1514 // This type was not registered before. |
|
1515 // append it to the array, without copy-constructing the string |
|
1516 nsCString *elem = mTypeArray.AppendElement(); |
|
1517 if (!elem) |
|
1518 return -1; |
|
1519 |
|
1520 elem->Assign(aType); |
|
1521 return mTypeArray.Length() - 1; |
|
1522 } |
|
1523 |
|
1524 // wrapper function for mangling (host,type,perm,expireType,expireTime) |
|
1525 // set into an nsIPermission. |
|
1526 void |
|
1527 nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost, |
|
1528 uint32_t aAppId, |
|
1529 bool aIsInBrowserElement, |
|
1530 const nsCString &aType, |
|
1531 uint32_t aPermission, |
|
1532 uint32_t aExpireType, |
|
1533 int64_t aExpireTime, |
|
1534 const char16_t *aData) |
|
1535 { |
|
1536 nsCOMPtr<nsIPermission> permission = |
|
1537 new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission, |
|
1538 aExpireType, aExpireTime); |
|
1539 if (permission) |
|
1540 NotifyObservers(permission, aData); |
|
1541 } |
|
1542 |
|
1543 // notify observers that the permission list changed. there are four possible |
|
1544 // values for aData: |
|
1545 // "deleted" means a permission was deleted. aPermission is the deleted permission. |
|
1546 // "added" means a permission was added. aPermission is the added permission. |
|
1547 // "changed" means a permission was altered. aPermission is the new permission. |
|
1548 // "cleared" means the entire permission list was cleared. aPermission is null. |
|
1549 void |
|
1550 nsPermissionManager::NotifyObservers(nsIPermission *aPermission, |
|
1551 const char16_t *aData) |
|
1552 { |
|
1553 if (mObserverService) |
|
1554 mObserverService->NotifyObservers(aPermission, |
|
1555 kPermissionChangeNotification, |
|
1556 aData); |
|
1557 } |
|
1558 |
|
1559 nsresult |
|
1560 nsPermissionManager::Read() |
|
1561 { |
|
1562 ENSURE_NOT_CHILD_PROCESS; |
|
1563 |
|
1564 nsresult rv; |
|
1565 |
|
1566 // delete expired permissions before we read in the db |
|
1567 { |
|
1568 // this deletion has its own scope so the write lock is released when done. |
|
1569 nsCOMPtr<mozIStorageStatement> stmtDeleteExpired; |
|
1570 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( |
|
1571 "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"), |
|
1572 getter_AddRefs(stmtDeleteExpired)); |
|
1573 NS_ENSURE_SUCCESS(rv, rv); |
|
1574 |
|
1575 rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME); |
|
1576 NS_ENSURE_SUCCESS(rv, rv); |
|
1577 |
|
1578 rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000); |
|
1579 NS_ENSURE_SUCCESS(rv, rv); |
|
1580 |
|
1581 bool hasResult; |
|
1582 rv = stmtDeleteExpired->ExecuteStep(&hasResult); |
|
1583 NS_ENSURE_SUCCESS(rv, rv); |
|
1584 } |
|
1585 |
|
1586 nsCOMPtr<mozIStorageStatement> stmt; |
|
1587 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( |
|
1588 "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement " |
|
1589 "FROM moz_hosts"), getter_AddRefs(stmt)); |
|
1590 NS_ENSURE_SUCCESS(rv, rv); |
|
1591 |
|
1592 int64_t id; |
|
1593 nsAutoCString host, type; |
|
1594 uint32_t permission; |
|
1595 uint32_t expireType; |
|
1596 int64_t expireTime; |
|
1597 uint32_t appId; |
|
1598 bool isInBrowserElement; |
|
1599 bool hasResult; |
|
1600 bool readError = false; |
|
1601 |
|
1602 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
|
1603 // explicitly set our entry id counter for use in AddInternal(), |
|
1604 // and keep track of the largest id so we know where to pick up. |
|
1605 id = stmt->AsInt64(0); |
|
1606 if (id > mLargestID) |
|
1607 mLargestID = id; |
|
1608 |
|
1609 rv = stmt->GetUTF8String(1, host); |
|
1610 if (NS_FAILED(rv)) { |
|
1611 readError = true; |
|
1612 continue; |
|
1613 } |
|
1614 |
|
1615 rv = stmt->GetUTF8String(2, type); |
|
1616 if (NS_FAILED(rv)) { |
|
1617 readError = true; |
|
1618 continue; |
|
1619 } |
|
1620 |
|
1621 permission = stmt->AsInt32(3); |
|
1622 expireType = stmt->AsInt32(4); |
|
1623 |
|
1624 // convert into int64_t value (milliseconds) |
|
1625 expireTime = stmt->AsInt64(5); |
|
1626 |
|
1627 if (stmt->AsInt64(6) < 0) { |
|
1628 readError = true; |
|
1629 continue; |
|
1630 } |
|
1631 appId = static_cast<uint32_t>(stmt->AsInt64(6)); |
|
1632 isInBrowserElement = static_cast<bool>(stmt->AsInt32(7)); |
|
1633 |
|
1634 nsCOMPtr<nsIPrincipal> principal; |
|
1635 nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal)); |
|
1636 if (NS_FAILED(rv)) { |
|
1637 readError = true; |
|
1638 continue; |
|
1639 } |
|
1640 |
|
1641 rv = AddInternal(principal, type, permission, id, expireType, expireTime, |
|
1642 eDontNotify, eNoDBOperation); |
|
1643 if (NS_FAILED(rv)) { |
|
1644 readError = true; |
|
1645 continue; |
|
1646 } |
|
1647 } |
|
1648 |
|
1649 if (readError) { |
|
1650 NS_ERROR("Error occured while reading the permissions database!"); |
|
1651 return NS_ERROR_FAILURE; |
|
1652 } |
|
1653 |
|
1654 return NS_OK; |
|
1655 } |
|
1656 |
|
1657 static const char kMatchTypeHost[] = "host"; |
|
1658 |
|
1659 nsresult |
|
1660 nsPermissionManager::Import() |
|
1661 { |
|
1662 ENSURE_NOT_CHILD_PROCESS; |
|
1663 |
|
1664 nsresult rv; |
|
1665 |
|
1666 nsCOMPtr<nsIFile> permissionsFile; |
|
1667 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile)); |
|
1668 if (NS_FAILED(rv)) return rv; |
|
1669 |
|
1670 rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName)); |
|
1671 NS_ENSURE_SUCCESS(rv, rv); |
|
1672 |
|
1673 nsCOMPtr<nsIInputStream> fileInputStream; |
|
1674 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), |
|
1675 permissionsFile); |
|
1676 if (NS_FAILED(rv)) return rv; |
|
1677 |
|
1678 nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv); |
|
1679 NS_ENSURE_SUCCESS(rv, rv); |
|
1680 |
|
1681 // start a transaction on the storage db, to optimize insertions. |
|
1682 // transaction will automically commit on completion |
|
1683 mozStorageTransaction transaction(mDBConn, true); |
|
1684 |
|
1685 /* format is: |
|
1686 * matchtype \t type \t permission \t host |
|
1687 * Only "host" is supported for matchtype |
|
1688 * type is a string that identifies the type of permission (e.g. "cookie") |
|
1689 * permission is an integer between 1 and 15 |
|
1690 */ |
|
1691 |
|
1692 nsAutoCString buffer; |
|
1693 bool isMore = true; |
|
1694 while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { |
|
1695 if (buffer.IsEmpty() || buffer.First() == '#') { |
|
1696 continue; |
|
1697 } |
|
1698 |
|
1699 nsTArray<nsCString> lineArray; |
|
1700 |
|
1701 // Split the line at tabs |
|
1702 ParseString(buffer, '\t', lineArray); |
|
1703 |
|
1704 if (lineArray[0].EqualsLiteral(kMatchTypeHost) && |
|
1705 lineArray.Length() == 4) { |
|
1706 |
|
1707 nsresult error; |
|
1708 uint32_t permission = lineArray[2].ToInteger(&error); |
|
1709 if (NS_FAILED(error)) |
|
1710 continue; |
|
1711 |
|
1712 // hosts might be encoded in UTF8; switch them to ACE to be consistent |
|
1713 if (!IsASCII(lineArray[3])) { |
|
1714 rv = NormalizeToACE(lineArray[3]); |
|
1715 if (NS_FAILED(rv)) |
|
1716 continue; |
|
1717 } |
|
1718 |
|
1719 nsCOMPtr<nsIPrincipal> principal; |
|
1720 nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal)); |
|
1721 NS_ENSURE_SUCCESS(rv, rv); |
|
1722 |
|
1723 rv = AddInternal(principal, lineArray[1], permission, 0, |
|
1724 nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB); |
|
1725 NS_ENSURE_SUCCESS(rv, rv); |
|
1726 } |
|
1727 } |
|
1728 |
|
1729 // we're done importing - delete the old file |
|
1730 permissionsFile->Remove(false); |
|
1731 |
|
1732 return NS_OK; |
|
1733 } |
|
1734 |
|
1735 nsresult |
|
1736 nsPermissionManager::NormalizeToACE(nsCString &aHost) |
|
1737 { |
|
1738 // lazily init the IDN service |
|
1739 if (!mIDNService) { |
|
1740 nsresult rv; |
|
1741 mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); |
|
1742 NS_ENSURE_SUCCESS(rv, rv); |
|
1743 } |
|
1744 |
|
1745 return mIDNService->ConvertUTF8toACE(aHost, aHost); |
|
1746 } |
|
1747 |
|
1748 void |
|
1749 nsPermissionManager::UpdateDB(OperationType aOp, |
|
1750 mozIStorageAsyncStatement* aStmt, |
|
1751 int64_t aID, |
|
1752 const nsACString &aHost, |
|
1753 const nsACString &aType, |
|
1754 uint32_t aPermission, |
|
1755 uint32_t aExpireType, |
|
1756 int64_t aExpireTime, |
|
1757 uint32_t aAppId, |
|
1758 bool aIsInBrowserElement) |
|
1759 { |
|
1760 ENSURE_NOT_CHILD_PROCESS_NORET; |
|
1761 |
|
1762 nsresult rv; |
|
1763 |
|
1764 // no statement is ok - just means we don't have a profile |
|
1765 if (!aStmt) |
|
1766 return; |
|
1767 |
|
1768 switch (aOp) { |
|
1769 case eOperationAdding: |
|
1770 { |
|
1771 rv = aStmt->BindInt64ByIndex(0, aID); |
|
1772 if (NS_FAILED(rv)) break; |
|
1773 |
|
1774 rv = aStmt->BindUTF8StringByIndex(1, aHost); |
|
1775 if (NS_FAILED(rv)) break; |
|
1776 |
|
1777 rv = aStmt->BindUTF8StringByIndex(2, aType); |
|
1778 if (NS_FAILED(rv)) break; |
|
1779 |
|
1780 rv = aStmt->BindInt32ByIndex(3, aPermission); |
|
1781 if (NS_FAILED(rv)) break; |
|
1782 |
|
1783 rv = aStmt->BindInt32ByIndex(4, aExpireType); |
|
1784 if (NS_FAILED(rv)) break; |
|
1785 |
|
1786 rv = aStmt->BindInt64ByIndex(5, aExpireTime); |
|
1787 if (NS_FAILED(rv)) break; |
|
1788 |
|
1789 rv = aStmt->BindInt64ByIndex(6, aAppId); |
|
1790 if (NS_FAILED(rv)) break; |
|
1791 |
|
1792 rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement); |
|
1793 break; |
|
1794 } |
|
1795 |
|
1796 case eOperationRemoving: |
|
1797 { |
|
1798 rv = aStmt->BindInt64ByIndex(0, aID); |
|
1799 break; |
|
1800 } |
|
1801 |
|
1802 case eOperationChanging: |
|
1803 { |
|
1804 rv = aStmt->BindInt64ByIndex(0, aID); |
|
1805 if (NS_FAILED(rv)) break; |
|
1806 |
|
1807 rv = aStmt->BindInt32ByIndex(1, aPermission); |
|
1808 if (NS_FAILED(rv)) break; |
|
1809 |
|
1810 rv = aStmt->BindInt32ByIndex(2, aExpireType); |
|
1811 if (NS_FAILED(rv)) break; |
|
1812 |
|
1813 rv = aStmt->BindInt64ByIndex(3, aExpireTime); |
|
1814 break; |
|
1815 } |
|
1816 |
|
1817 default: |
|
1818 { |
|
1819 NS_NOTREACHED("need a valid operation in UpdateDB()!"); |
|
1820 rv = NS_ERROR_UNEXPECTED; |
|
1821 break; |
|
1822 } |
|
1823 } |
|
1824 |
|
1825 if (NS_FAILED(rv)) { |
|
1826 NS_WARNING("db change failed!"); |
|
1827 return; |
|
1828 } |
|
1829 |
|
1830 nsCOMPtr<mozIStoragePendingStatement> pending; |
|
1831 rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); |
|
1832 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
1833 } |
|
1834 |
|
1835 NS_IMETHODIMP |
|
1836 nsPermissionManager::AddrefAppId(uint32_t aAppId) |
|
1837 { |
|
1838 if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { |
|
1839 return NS_OK; |
|
1840 } |
|
1841 |
|
1842 bool found = false; |
|
1843 for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) { |
|
1844 if (mAppIdRefcounts[i].mAppId == aAppId) { |
|
1845 ++mAppIdRefcounts[i].mCounter; |
|
1846 found = true; |
|
1847 break; |
|
1848 } |
|
1849 } |
|
1850 |
|
1851 if (!found) { |
|
1852 ApplicationCounter app = { aAppId, 1 }; |
|
1853 mAppIdRefcounts.AppendElement(app); |
|
1854 } |
|
1855 |
|
1856 return NS_OK; |
|
1857 } |
|
1858 |
|
1859 NS_IMETHODIMP |
|
1860 nsPermissionManager::ReleaseAppId(uint32_t aAppId) |
|
1861 { |
|
1862 // An app has been released, maybe we have to reset its session. |
|
1863 |
|
1864 if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { |
|
1865 return NS_OK; |
|
1866 } |
|
1867 |
|
1868 for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) { |
|
1869 if (mAppIdRefcounts[i].mAppId == aAppId) { |
|
1870 --mAppIdRefcounts[i].mCounter; |
|
1871 |
|
1872 if (!mAppIdRefcounts[i].mCounter) { |
|
1873 mAppIdRefcounts.RemoveElementAt(i); |
|
1874 return RemoveExpiredPermissionsForApp(aAppId); |
|
1875 } |
|
1876 |
|
1877 break; |
|
1878 } |
|
1879 } |
|
1880 |
|
1881 return NS_OK; |
|
1882 } |
|
1883 |
|
1884 NS_IMETHODIMP |
|
1885 nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal, |
|
1886 const char* aType, |
|
1887 bool aExactHostMatch, |
|
1888 uint64_t aSessionExpireTime, |
|
1889 uint64_t aPersistentExpireTime) |
|
1890 { |
|
1891 NS_ENSURE_ARG_POINTER(aPrincipal); |
|
1892 NS_ENSURE_ARG_POINTER(aType); |
|
1893 |
|
1894 uint64_t nowms = PR_Now() / 1000; |
|
1895 if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) { |
|
1896 return NS_ERROR_INVALID_ARG; |
|
1897 } |
|
1898 |
|
1899 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
|
1900 return NS_OK; |
|
1901 } |
|
1902 |
|
1903 // Setting the expire time of an nsEP is non-sensical. |
|
1904 if (IsExpandedPrincipal(aPrincipal)) { |
|
1905 return NS_ERROR_INVALID_ARG; |
|
1906 } |
|
1907 |
|
1908 nsAutoCString host; |
|
1909 nsresult rv = GetHostForPrincipal(aPrincipal, host); |
|
1910 NS_ENSURE_SUCCESS(rv, rv); |
|
1911 |
|
1912 int32_t typeIndex = GetTypeIndex(aType, false); |
|
1913 // If type == -1, the type isn't known, |
|
1914 // so just return NS_OK |
|
1915 if (typeIndex == -1) return NS_OK; |
|
1916 |
|
1917 uint32_t appId; |
|
1918 rv = aPrincipal->GetAppId(&appId); |
|
1919 NS_ENSURE_SUCCESS(rv, rv); |
|
1920 |
|
1921 bool isInBrowserElement; |
|
1922 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
|
1923 NS_ENSURE_SUCCESS(rv, rv); |
|
1924 |
|
1925 PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, |
|
1926 typeIndex, aExactHostMatch); |
|
1927 if (!entry) { |
|
1928 return NS_OK; |
|
1929 } |
|
1930 |
|
1931 int32_t idx = entry->GetPermissionIndex(typeIndex); |
|
1932 if (-1 == idx) { |
|
1933 return NS_OK; |
|
1934 } |
|
1935 |
|
1936 PermissionEntry& perm = entry->GetPermissions()[idx]; |
|
1937 if (perm.mExpireType == EXPIRE_TIME) { |
|
1938 perm.mExpireTime = aPersistentExpireTime; |
|
1939 } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) { |
|
1940 perm.mExpireTime = aSessionExpireTime; |
|
1941 } |
|
1942 return NS_OK; |
|
1943 } |