|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const Ci = Components.interfaces; |
|
8 const Cu = Components.utils; |
|
9 |
|
10 this.EXPORTED_SYMBOLS = [ |
|
11 "PermissionsTable", |
|
12 "PermissionsReverseTable", |
|
13 "expandPermissions", |
|
14 "appendAccessToPermName", |
|
15 "isExplicitInPermissionsTable" |
|
16 ]; |
|
17 |
|
18 // Permission access flags |
|
19 const READONLY = "readonly"; |
|
20 const CREATEONLY = "createonly"; |
|
21 const READCREATE = "readcreate"; |
|
22 const READWRITE = "readwrite"; |
|
23 |
|
24 const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION; |
|
25 const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION; |
|
26 const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION; |
|
27 const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION; |
|
28 |
|
29 // Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0 |
|
30 |
|
31 // Permissions that are implicit: |
|
32 // battery-status, network-information, vibration, |
|
33 // device-capabilities |
|
34 |
|
35 this.PermissionsTable = { geolocation: { |
|
36 app: PROMPT_ACTION, |
|
37 privileged: PROMPT_ACTION, |
|
38 certified: PROMPT_ACTION |
|
39 }, |
|
40 camera: { |
|
41 app: DENY_ACTION, |
|
42 privileged: PROMPT_ACTION, |
|
43 certified: ALLOW_ACTION |
|
44 }, |
|
45 alarms: { |
|
46 app: ALLOW_ACTION, |
|
47 privileged: ALLOW_ACTION, |
|
48 certified: ALLOW_ACTION |
|
49 }, |
|
50 "tcp-socket": { |
|
51 app: DENY_ACTION, |
|
52 privileged: ALLOW_ACTION, |
|
53 certified: ALLOW_ACTION |
|
54 }, |
|
55 "network-events": { |
|
56 app: DENY_ACTION, |
|
57 privileged: DENY_ACTION, |
|
58 certified: ALLOW_ACTION |
|
59 }, |
|
60 contacts: { |
|
61 app: DENY_ACTION, |
|
62 privileged: PROMPT_ACTION, |
|
63 certified: ALLOW_ACTION, |
|
64 access: ["read", "write", "create"] |
|
65 }, |
|
66 "device-storage:apps": { |
|
67 app: DENY_ACTION, |
|
68 privileged: DENY_ACTION, |
|
69 certified: ALLOW_ACTION, |
|
70 access: ["read"] |
|
71 }, |
|
72 "device-storage:crashes": { |
|
73 app: DENY_ACTION, |
|
74 privileged: DENY_ACTION, |
|
75 certified: ALLOW_ACTION, |
|
76 access: ["read"] |
|
77 }, |
|
78 "device-storage:pictures": { |
|
79 app: DENY_ACTION, |
|
80 privileged: PROMPT_ACTION, |
|
81 certified: ALLOW_ACTION, |
|
82 access: ["read", "write", "create"] |
|
83 }, |
|
84 "device-storage:videos": { |
|
85 app: DENY_ACTION, |
|
86 privileged: PROMPT_ACTION, |
|
87 certified: ALLOW_ACTION, |
|
88 access: ["read", "write", "create"] |
|
89 }, |
|
90 "device-storage:music": { |
|
91 app: DENY_ACTION, |
|
92 privileged: PROMPT_ACTION, |
|
93 certified: ALLOW_ACTION, |
|
94 access: ["read", "write", "create"] |
|
95 }, |
|
96 "device-storage:sdcard": { |
|
97 app: DENY_ACTION, |
|
98 privileged: PROMPT_ACTION, |
|
99 certified: ALLOW_ACTION, |
|
100 access: ["read", "write", "create"] |
|
101 }, |
|
102 sms: { |
|
103 app: DENY_ACTION, |
|
104 privileged: DENY_ACTION, |
|
105 certified: ALLOW_ACTION |
|
106 }, |
|
107 telephony: { |
|
108 app: DENY_ACTION, |
|
109 privileged: DENY_ACTION, |
|
110 certified: ALLOW_ACTION |
|
111 }, |
|
112 browser: { |
|
113 app: DENY_ACTION, |
|
114 privileged: ALLOW_ACTION, |
|
115 certified: ALLOW_ACTION |
|
116 }, |
|
117 bluetooth: { |
|
118 app: DENY_ACTION, |
|
119 privileged: DENY_ACTION, |
|
120 certified: ALLOW_ACTION |
|
121 }, |
|
122 mobileconnection: { |
|
123 app: DENY_ACTION, |
|
124 privileged: DENY_ACTION, |
|
125 certified: ALLOW_ACTION |
|
126 }, |
|
127 mobilenetwork: { |
|
128 app: DENY_ACTION, |
|
129 privileged: ALLOW_ACTION, |
|
130 certified: ALLOW_ACTION |
|
131 }, |
|
132 power: { |
|
133 app: DENY_ACTION, |
|
134 privileged: DENY_ACTION, |
|
135 certified: ALLOW_ACTION |
|
136 }, |
|
137 push: { |
|
138 app: ALLOW_ACTION, |
|
139 privileged: ALLOW_ACTION, |
|
140 certified: ALLOW_ACTION |
|
141 }, |
|
142 settings: { |
|
143 app: DENY_ACTION, |
|
144 privileged: DENY_ACTION, |
|
145 certified: ALLOW_ACTION, |
|
146 access: ["read", "write"], |
|
147 additional: ["indexedDB-chrome-settings"] |
|
148 }, |
|
149 permissions: { |
|
150 app: DENY_ACTION, |
|
151 privileged: DENY_ACTION, |
|
152 certified: ALLOW_ACTION |
|
153 }, |
|
154 phonenumberservice: { |
|
155 app: DENY_ACTION, |
|
156 privileged: DENY_ACTION, |
|
157 certified: ALLOW_ACTION |
|
158 }, |
|
159 fmradio: { |
|
160 app: DENY_ACTION, |
|
161 privileged: ALLOW_ACTION, |
|
162 certified: ALLOW_ACTION |
|
163 }, |
|
164 attention: { |
|
165 app: DENY_ACTION, |
|
166 privileged: DENY_ACTION, |
|
167 certified: ALLOW_ACTION |
|
168 }, |
|
169 "webapps-manage": { |
|
170 app: DENY_ACTION, |
|
171 privileged: DENY_ACTION, |
|
172 certified: ALLOW_ACTION |
|
173 }, |
|
174 "backgroundservice": { |
|
175 app: DENY_ACTION, |
|
176 privileged: DENY_ACTION, |
|
177 certified: ALLOW_ACTION |
|
178 }, |
|
179 "desktop-notification": { |
|
180 app: ALLOW_ACTION, |
|
181 privileged: ALLOW_ACTION, |
|
182 certified: ALLOW_ACTION |
|
183 }, |
|
184 "networkstats-manage": { |
|
185 app: DENY_ACTION, |
|
186 privileged: DENY_ACTION, |
|
187 certified: ALLOW_ACTION |
|
188 }, |
|
189 "wifi-manage": { |
|
190 app: DENY_ACTION, |
|
191 privileged: DENY_ACTION, |
|
192 certified: ALLOW_ACTION |
|
193 }, |
|
194 "systemXHR": { |
|
195 app: DENY_ACTION, |
|
196 privileged: ALLOW_ACTION, |
|
197 certified: ALLOW_ACTION |
|
198 }, |
|
199 "voicemail": { |
|
200 app: DENY_ACTION, |
|
201 privileged: DENY_ACTION, |
|
202 certified: ALLOW_ACTION |
|
203 }, |
|
204 "deprecated-hwvideo": { |
|
205 app: DENY_ACTION, |
|
206 privileged: DENY_ACTION, |
|
207 certified: ALLOW_ACTION |
|
208 }, |
|
209 "idle": { |
|
210 app: DENY_ACTION, |
|
211 privileged: DENY_ACTION, |
|
212 certified: ALLOW_ACTION |
|
213 }, |
|
214 "time": { |
|
215 app: DENY_ACTION, |
|
216 privileged: DENY_ACTION, |
|
217 certified: ALLOW_ACTION |
|
218 }, |
|
219 "embed-apps": { |
|
220 app: DENY_ACTION, |
|
221 privileged: DENY_ACTION, |
|
222 certified: ALLOW_ACTION |
|
223 }, |
|
224 "storage": { |
|
225 app: ALLOW_ACTION, |
|
226 privileged: ALLOW_ACTION, |
|
227 certified: ALLOW_ACTION, |
|
228 substitute: [ |
|
229 "indexedDB-unlimited", |
|
230 "default-persistent-storage" |
|
231 ] |
|
232 }, |
|
233 "background-sensors": { |
|
234 app: DENY_ACTION, |
|
235 privileged: DENY_ACTION, |
|
236 certified: ALLOW_ACTION |
|
237 }, |
|
238 cellbroadcast: { |
|
239 app: DENY_ACTION, |
|
240 privileged: DENY_ACTION, |
|
241 certified: ALLOW_ACTION |
|
242 }, |
|
243 "audio-channel-normal": { |
|
244 app: ALLOW_ACTION, |
|
245 privileged: ALLOW_ACTION, |
|
246 certified: ALLOW_ACTION |
|
247 }, |
|
248 "audio-channel-content": { |
|
249 app: ALLOW_ACTION, |
|
250 privileged: ALLOW_ACTION, |
|
251 certified: ALLOW_ACTION |
|
252 }, |
|
253 "audio-channel-notification": { |
|
254 app: DENY_ACTION, |
|
255 privileged: ALLOW_ACTION, |
|
256 certified: ALLOW_ACTION |
|
257 }, |
|
258 "audio-channel-alarm": { |
|
259 app: DENY_ACTION, |
|
260 privileged: ALLOW_ACTION, |
|
261 certified: ALLOW_ACTION |
|
262 }, |
|
263 "audio-channel-telephony": { |
|
264 app: DENY_ACTION, |
|
265 privileged: DENY_ACTION, |
|
266 certified: ALLOW_ACTION |
|
267 }, |
|
268 "audio-channel-ringer": { |
|
269 app: DENY_ACTION, |
|
270 privileged: DENY_ACTION, |
|
271 certified: ALLOW_ACTION |
|
272 }, |
|
273 "audio-channel-publicnotification": { |
|
274 app: DENY_ACTION, |
|
275 privileged: DENY_ACTION, |
|
276 certified: ALLOW_ACTION |
|
277 }, |
|
278 "open-remote-window": { |
|
279 app: DENY_ACTION, |
|
280 privileged: DENY_ACTION, |
|
281 certified: ALLOW_ACTION |
|
282 }, |
|
283 "input": { |
|
284 app: DENY_ACTION, |
|
285 privileged: ALLOW_ACTION, |
|
286 certified: ALLOW_ACTION |
|
287 }, |
|
288 "input-manage": { |
|
289 app: DENY_ACTION, |
|
290 privileged: DENY_ACTION, |
|
291 certified: ALLOW_ACTION |
|
292 }, |
|
293 "wappush": { |
|
294 app: DENY_ACTION, |
|
295 privileged: DENY_ACTION, |
|
296 certified: ALLOW_ACTION |
|
297 }, |
|
298 "audio-capture": { |
|
299 app: PROMPT_ACTION, |
|
300 privileged: PROMPT_ACTION, |
|
301 certified: PROMPT_ACTION |
|
302 }, |
|
303 "nfc": { |
|
304 app: DENY_ACTION, |
|
305 privileged: DENY_ACTION, |
|
306 certified: ALLOW_ACTION, |
|
307 access: ["read", "write"] |
|
308 }, |
|
309 "nfc-manager": { |
|
310 app: DENY_ACTION, |
|
311 privileged: DENY_ACTION, |
|
312 certified: ALLOW_ACTION |
|
313 }, |
|
314 "speaker-control": { |
|
315 app: DENY_ACTION, |
|
316 privileged: ALLOW_ACTION, |
|
317 certified: ALLOW_ACTION |
|
318 }, |
|
319 "downloads": { |
|
320 app: DENY_ACTION, |
|
321 privileged: DENY_ACTION, |
|
322 certified: ALLOW_ACTION |
|
323 }, |
|
324 "video-capture": { |
|
325 app: PROMPT_ACTION, |
|
326 privileged: PROMPT_ACTION, |
|
327 certified: PROMPT_ACTION |
|
328 }, |
|
329 }; |
|
330 |
|
331 /** |
|
332 * Append access modes to the permission name as suffixes. |
|
333 * e.g. permission name 'contacts' with ['read', 'write'] = |
|
334 * ['contacts-read', contacts-write'] |
|
335 * @param string aPermName |
|
336 * @param array aAccess |
|
337 * @returns array containing access-appended permission names. |
|
338 **/ |
|
339 this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) { |
|
340 if (aAccess.length == 0) { |
|
341 return [aPermName]; |
|
342 } |
|
343 return aAccess.map(function(aMode) { |
|
344 return aPermName + "-" + aMode; |
|
345 }); |
|
346 }; |
|
347 |
|
348 /** |
|
349 * Expand an access string into multiple permission names, |
|
350 * e.g: permission name 'contacts' with 'readwrite' = |
|
351 * ['contacts-read', 'contacts-create', 'contacts-write'] |
|
352 * @param string aPermName |
|
353 * @param string aAccess (optional) |
|
354 * @returns array containing expanded permission names. |
|
355 **/ |
|
356 this.expandPermissions = function expandPermissions(aPermName, aAccess) { |
|
357 if (!PermissionsTable[aPermName]) { |
|
358 let errorMsg = |
|
359 "PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName; |
|
360 Cu.reportError(errorMsg); |
|
361 dump(errorMsg); |
|
362 return []; |
|
363 } |
|
364 |
|
365 const tableEntry = PermissionsTable[aPermName]; |
|
366 |
|
367 if (tableEntry.substitute && tableEntry.additional) { |
|
368 let errorMsg = |
|
369 "PermissionsTable.jsm: expandPermissions: Can't handle both 'substitute' " + |
|
370 "and 'additional' entries for permission: " + aPermName; |
|
371 Cu.reportError(errorMsg); |
|
372 dump(errorMsg); |
|
373 return []; |
|
374 } |
|
375 |
|
376 if (!aAccess && tableEntry.access || |
|
377 aAccess && !tableEntry.access) { |
|
378 let errorMsg = |
|
379 "PermissionsTable.jsm: expandPermissions: Invalid access for permission " + |
|
380 aPermName + ": " + aAccess + "\n"; |
|
381 Cu.reportError(errorMsg); |
|
382 dump(errorMsg); |
|
383 return []; |
|
384 } |
|
385 |
|
386 let expandedPermNames = []; |
|
387 |
|
388 if (tableEntry.access && aAccess) { |
|
389 let requestedSuffixes = []; |
|
390 switch (aAccess) { |
|
391 case READONLY: |
|
392 requestedSuffixes.push("read"); |
|
393 break; |
|
394 case CREATEONLY: |
|
395 requestedSuffixes.push("create"); |
|
396 break; |
|
397 case READCREATE: |
|
398 requestedSuffixes.push("read", "create"); |
|
399 break; |
|
400 case READWRITE: |
|
401 requestedSuffixes.push("read", "create", "write"); |
|
402 break; |
|
403 default: |
|
404 return []; |
|
405 } |
|
406 |
|
407 let permArr = appendAccessToPermName(aPermName, requestedSuffixes); |
|
408 |
|
409 // Add the same suffix to each of the additions. |
|
410 if (tableEntry.additional) { |
|
411 for each (let additional in tableEntry.additional) { |
|
412 permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes)); |
|
413 } |
|
414 } |
|
415 |
|
416 // Only add the suffixed version if the suffix exists in the table. |
|
417 for (let idx in permArr) { |
|
418 let suffix = requestedSuffixes[idx % requestedSuffixes.length]; |
|
419 if (tableEntry.access.indexOf(suffix) != -1) { |
|
420 expandedPermNames.push(permArr[idx]); |
|
421 } |
|
422 } |
|
423 } else if (tableEntry.substitute) { |
|
424 expandedPermNames = expandedPermNames.concat(tableEntry.substitute); |
|
425 } else { |
|
426 expandedPermNames.push(aPermName); |
|
427 // Include each of the additions exactly as they appear in the table. |
|
428 if (tableEntry.additional) { |
|
429 expandedPermNames = expandedPermNames.concat(tableEntry.additional); |
|
430 } |
|
431 } |
|
432 |
|
433 return expandedPermNames; |
|
434 }; |
|
435 |
|
436 this.PermissionsReverseTable = (function () { |
|
437 // PermissionsTable as it is works well for direct searches, but not |
|
438 // so well for reverse ones (that is, if I get something like |
|
439 // device-storage:music-read or indexedDB-chrome-settings-read how |
|
440 // do I know which permission it really is? Hence this table is |
|
441 // born. The idea is that |
|
442 // reverseTable[device-storage:music-read] should return |
|
443 // device-storage:music |
|
444 let reverseTable = {}; |
|
445 |
|
446 for (let permName in PermissionsTable) { |
|
447 let permAliases; |
|
448 if (PermissionsTable[permName].access) { |
|
449 permAliases = expandPermissions(permName, "readwrite"); |
|
450 } else { |
|
451 permAliases = expandPermissions(permName); |
|
452 } |
|
453 for (let i = 0; i < permAliases.length; i++) { |
|
454 reverseTable[permAliases[i]] = permName; |
|
455 } |
|
456 } |
|
457 |
|
458 return reverseTable; |
|
459 |
|
460 })(); |
|
461 |
|
462 this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) { |
|
463 |
|
464 // Check to see if the 'webapp' is app/privileged/certified. |
|
465 let appStatus; |
|
466 switch (aIntStatus) { |
|
467 case Ci.nsIPrincipal.APP_STATUS_CERTIFIED: |
|
468 appStatus = "certified"; |
|
469 break; |
|
470 case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED: |
|
471 appStatus = "privileged"; |
|
472 break; |
|
473 default: // If it isn't certified or privileged, it's app |
|
474 appStatus = "app"; |
|
475 break; |
|
476 } |
|
477 |
|
478 let realPerm = PermissionsReverseTable[aPermName]; |
|
479 |
|
480 if (realPerm) { |
|
481 return (PermissionsTable[realPerm][appStatus] == |
|
482 Ci.nsIPermissionManager.PROMPT_ACTION); |
|
483 } else { |
|
484 return false; |
|
485 } |
|
486 } |