PermissionsTable.jsm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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. "use strict";
  5. const Ci = Components.interfaces;
  6. const Cu = Components.utils;
  7. this.EXPORTED_SYMBOLS = [
  8. "PermissionsTable",
  9. "PermissionsReverseTable",
  10. "expandPermissions",
  11. "appendAccessToPermName",
  12. "isExplicitInPermissionsTable",
  13. "AllPossiblePermissions"
  14. ];
  15. // Permission access flags
  16. const READONLY = "readonly";
  17. const CREATEONLY = "createonly";
  18. const READCREATE = "readcreate";
  19. const READWRITE = "readwrite";
  20. const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION;
  21. const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION;
  22. const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION;
  23. const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION;
  24. // Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0
  25. // Permissions that are implicit:
  26. // battery-status, network-information, vibration,
  27. // device-capabilities
  28. this.PermissionsTable = { geolocation: {
  29. app: PROMPT_ACTION,
  30. privileged: PROMPT_ACTION,
  31. certified: PROMPT_ACTION
  32. },
  33. "geolocation-noprompt": {
  34. app: DENY_ACTION,
  35. privileged: DENY_ACTION,
  36. certified: ALLOW_ACTION,
  37. substitute: ["geolocation"]
  38. },
  39. camera: {
  40. app: DENY_ACTION,
  41. privileged: PROMPT_ACTION,
  42. certified: ALLOW_ACTION
  43. },
  44. alarms: {
  45. app: ALLOW_ACTION,
  46. privileged: ALLOW_ACTION,
  47. certified: ALLOW_ACTION
  48. },
  49. "tcp-socket": {
  50. app: DENY_ACTION,
  51. privileged: ALLOW_ACTION,
  52. certified: ALLOW_ACTION
  53. },
  54. "udp-socket": {
  55. app: DENY_ACTION,
  56. privileged: ALLOW_ACTION,
  57. certified: ALLOW_ACTION
  58. },
  59. "network-events": {
  60. app: DENY_ACTION,
  61. privileged: DENY_ACTION,
  62. certified: ALLOW_ACTION
  63. },
  64. contacts: {
  65. app: DENY_ACTION,
  66. privileged: PROMPT_ACTION,
  67. certified: ALLOW_ACTION,
  68. access: ["read", "write", "create"]
  69. },
  70. "device-storage:apps": {
  71. app: DENY_ACTION,
  72. privileged: DENY_ACTION,
  73. certified: ALLOW_ACTION,
  74. access: ["read"]
  75. },
  76. "device-storage:crashes": {
  77. app: DENY_ACTION,
  78. privileged: DENY_ACTION,
  79. certified: ALLOW_ACTION,
  80. access: ["read"]
  81. },
  82. "device-storage:pictures": {
  83. app: DENY_ACTION,
  84. privileged: PROMPT_ACTION,
  85. certified: ALLOW_ACTION,
  86. access: ["read", "write", "create"]
  87. },
  88. "device-storage:videos": {
  89. app: DENY_ACTION,
  90. privileged: PROMPT_ACTION,
  91. certified: ALLOW_ACTION,
  92. access: ["read", "write", "create"]
  93. },
  94. "device-storage:music": {
  95. app: DENY_ACTION,
  96. privileged: PROMPT_ACTION,
  97. certified: ALLOW_ACTION,
  98. access: ["read", "write", "create"]
  99. },
  100. "device-storage:sdcard": {
  101. app: DENY_ACTION,
  102. privileged: PROMPT_ACTION,
  103. certified: ALLOW_ACTION,
  104. access: ["read", "write", "create"]
  105. },
  106. sms: {
  107. app: DENY_ACTION,
  108. privileged: DENY_ACTION,
  109. certified: ALLOW_ACTION
  110. },
  111. "speech-recognition": {
  112. app: DENY_ACTION,
  113. privileged: ALLOW_ACTION,
  114. certified: ALLOW_ACTION
  115. },
  116. browser: {
  117. app: DENY_ACTION,
  118. privileged: ALLOW_ACTION,
  119. certified: ALLOW_ACTION
  120. },
  121. "browser:universalxss": {
  122. app: DENY_ACTION,
  123. privileged: ALLOW_ACTION,
  124. certified: ALLOW_ACTION
  125. },
  126. "browser:embedded-system-app": {
  127. app: DENY_ACTION,
  128. privileged: DENY_ACTION,
  129. certified: ALLOW_ACTION
  130. },
  131. mobilenetwork: {
  132. app: DENY_ACTION,
  133. privileged: ALLOW_ACTION,
  134. certified: ALLOW_ACTION
  135. },
  136. power: {
  137. app: DENY_ACTION,
  138. privileged: DENY_ACTION,
  139. certified: ALLOW_ACTION
  140. },
  141. push: {
  142. app: ALLOW_ACTION,
  143. privileged: ALLOW_ACTION,
  144. certified: ALLOW_ACTION
  145. },
  146. settings: {
  147. app: DENY_ACTION,
  148. privileged: DENY_ACTION,
  149. certified: ALLOW_ACTION,
  150. access: ["read", "write"],
  151. additional: ["indexedDB-chrome-settings", "settings-api"]
  152. },
  153. // This exists purely for tests, no app
  154. // should ever use it. It can only be
  155. // handed out by SpecialPowers.
  156. "settings-clear": {
  157. app: DENY_ACTION,
  158. privileged: DENY_ACTION,
  159. certified: DENY_ACTION,
  160. additional: ["indexedDB-chrome-settings", "settings-api"]
  161. },
  162. permissions: {
  163. app: DENY_ACTION,
  164. privileged: DENY_ACTION,
  165. certified: ALLOW_ACTION
  166. },
  167. attention: {
  168. app: DENY_ACTION,
  169. privileged: DENY_ACTION,
  170. certified: ALLOW_ACTION
  171. },
  172. "global-clickthrough-overlay": {
  173. app: DENY_ACTION,
  174. privileged: ALLOW_ACTION,
  175. certified: ALLOW_ACTION
  176. },
  177. "moz-attention": {
  178. app: DENY_ACTION,
  179. privileged: ALLOW_ACTION,
  180. certified: ALLOW_ACTION,
  181. substitute: ["attention"]
  182. },
  183. "webapps-manage": {
  184. app: DENY_ACTION,
  185. privileged: DENY_ACTION,
  186. certified: ALLOW_ACTION
  187. },
  188. "homescreen-webapps-manage": {
  189. app: DENY_ACTION,
  190. privileged: ALLOW_ACTION,
  191. certified: ALLOW_ACTION
  192. },
  193. "backgroundservice": {
  194. app: DENY_ACTION,
  195. privileged: DENY_ACTION,
  196. certified: ALLOW_ACTION
  197. },
  198. "desktop-notification": {
  199. app: ALLOW_ACTION,
  200. privileged: ALLOW_ACTION,
  201. certified: ALLOW_ACTION
  202. },
  203. "networkstats-manage": {
  204. app: DENY_ACTION,
  205. privileged: DENY_ACTION,
  206. certified: ALLOW_ACTION
  207. },
  208. "wifi-manage": {
  209. app: DENY_ACTION,
  210. privileged: DENY_ACTION,
  211. certified: ALLOW_ACTION
  212. },
  213. "systemXHR": {
  214. app: DENY_ACTION,
  215. privileged: ALLOW_ACTION,
  216. certified: ALLOW_ACTION
  217. },
  218. "idle": {
  219. app: DENY_ACTION,
  220. privileged: DENY_ACTION,
  221. certified: ALLOW_ACTION
  222. },
  223. "time": {
  224. app: DENY_ACTION,
  225. privileged: DENY_ACTION,
  226. certified: ALLOW_ACTION
  227. },
  228. "embed-apps": {
  229. app: DENY_ACTION,
  230. privileged: DENY_ACTION,
  231. certified: ALLOW_ACTION
  232. },
  233. "background-sensors": {
  234. app: DENY_ACTION,
  235. privileged: DENY_ACTION,
  236. certified: ALLOW_ACTION
  237. },
  238. "audio-channel-normal": {
  239. app: ALLOW_ACTION,
  240. privileged: ALLOW_ACTION,
  241. certified: ALLOW_ACTION
  242. },
  243. "audio-channel-content": {
  244. app: ALLOW_ACTION,
  245. privileged: ALLOW_ACTION,
  246. certified: ALLOW_ACTION
  247. },
  248. "audio-channel-notification": {
  249. app: DENY_ACTION,
  250. privileged: ALLOW_ACTION,
  251. certified: ALLOW_ACTION
  252. },
  253. "audio-channel-alarm": {
  254. app: DENY_ACTION,
  255. privileged: ALLOW_ACTION,
  256. certified: ALLOW_ACTION
  257. },
  258. "audio-channel-system": {
  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. "moz-audio-channel-telephony": {
  269. app: DENY_ACTION,
  270. privileged: ALLOW_ACTION,
  271. certified: ALLOW_ACTION,
  272. substitute: ["audio-channel-telephony"]
  273. },
  274. "audio-channel-ringer": {
  275. app: DENY_ACTION,
  276. privileged: DENY_ACTION,
  277. certified: ALLOW_ACTION
  278. },
  279. "moz-audio-channel-ringer": {
  280. app: DENY_ACTION,
  281. privileged: ALLOW_ACTION,
  282. certified: ALLOW_ACTION,
  283. substitute: ["audio-channel-ringer"]
  284. },
  285. "audio-channel-publicnotification": {
  286. app: DENY_ACTION,
  287. privileged: DENY_ACTION,
  288. certified: ALLOW_ACTION
  289. },
  290. "open-remote-window": {
  291. app: DENY_ACTION,
  292. privileged: DENY_ACTION,
  293. certified: ALLOW_ACTION
  294. },
  295. "input": {
  296. app: DENY_ACTION,
  297. privileged: ALLOW_ACTION,
  298. certified: ALLOW_ACTION
  299. },
  300. "input-manage": {
  301. app: DENY_ACTION,
  302. privileged: DENY_ACTION,
  303. certified: ALLOW_ACTION
  304. },
  305. "audio-capture": {
  306. app: PROMPT_ACTION,
  307. privileged: PROMPT_ACTION,
  308. certified: ALLOW_ACTION
  309. },
  310. "audio-capture:3gpp": {
  311. app: DENY_ACTION,
  312. privileged: ALLOW_ACTION,
  313. certified: ALLOW_ACTION
  314. },
  315. "audio-capture:3gpp2": {
  316. app: DENY_ACTION,
  317. privileged: ALLOW_ACTION,
  318. certified: ALLOW_ACTION
  319. },
  320. "speaker-control": {
  321. app: DENY_ACTION,
  322. privileged: ALLOW_ACTION,
  323. certified: ALLOW_ACTION
  324. },
  325. "downloads": {
  326. app: DENY_ACTION,
  327. privileged: DENY_ACTION,
  328. certified: ALLOW_ACTION
  329. },
  330. "video-capture": {
  331. app: PROMPT_ACTION,
  332. privileged: PROMPT_ACTION,
  333. certified: ALLOW_ACTION
  334. },
  335. "feature-detection": {
  336. app: DENY_ACTION,
  337. privileged: ALLOW_ACTION,
  338. certified: ALLOW_ACTION
  339. },
  340. // This permission doesn't actually grant access to
  341. // anything. It exists only to check the correctness
  342. // of web prompt composed permissions in tests.
  343. "test-permission": {
  344. app: PROMPT_ACTION,
  345. privileged: PROMPT_ACTION,
  346. certified: ALLOW_ACTION,
  347. access: ["read", "write", "create"]
  348. },
  349. "firefox-accounts": {
  350. app: DENY_ACTION,
  351. privileged: DENY_ACTION,
  352. certified: ALLOW_ACTION
  353. },
  354. "moz-firefox-accounts": {
  355. app: DENY_ACTION,
  356. privileged: PROMPT_ACTION,
  357. certified: ALLOW_ACTION,
  358. substitute: ["firefox-accounts"]
  359. },
  360. "themeable": {
  361. app: DENY_ACTION,
  362. privileged: DENY_ACTION,
  363. certified: ALLOW_ACTION
  364. },
  365. "settings:wallpaper.image": {
  366. app: DENY_ACTION,
  367. privileged: ALLOW_ACTION,
  368. certified: ALLOW_ACTION,
  369. access: ["read", "write"],
  370. additional: ["settings-api"]
  371. },
  372. "tv": {
  373. app: DENY_ACTION,
  374. privileged: DENY_ACTION,
  375. certified: ALLOW_ACTION
  376. },
  377. "before-after-keyboard-event": {
  378. app: DENY_ACTION,
  379. privileged: DENY_ACTION,
  380. certified: ALLOW_ACTION
  381. },
  382. "presentation-device-manage": {
  383. app: DENY_ACTION,
  384. privileged: DENY_ACTION,
  385. certified: ALLOW_ACTION
  386. },
  387. "secureelement-manage": {
  388. app: DENY_ACTION,
  389. privileged: DENY_ACTION,
  390. certified: ALLOW_ACTION
  391. },
  392. "inputport": {
  393. app: DENY_ACTION,
  394. privileged: DENY_ACTION,
  395. certified: ALLOW_ACTION
  396. },
  397. "system-update": {
  398. app: DENY_ACTION,
  399. privileged: DENY_ACTION,
  400. certified: ALLOW_ACTION
  401. },
  402. "open-hidden-window": {
  403. app: DENY_ACTION,
  404. privileged: DENY_ACTION,
  405. certified: ALLOW_ACTION
  406. },
  407. "moz-extremely-unstable-and-will-change-webcomponents": {
  408. app: DENY_ACTION,
  409. trusted: DENY_ACTION,
  410. privileged: ALLOW_ACTION,
  411. certified: ALLOW_ACTION
  412. },
  413. "system-app-only-audio-channels-in-app": {
  414. app: DENY_ACTION,
  415. privileged: DENY_ACTION,
  416. certified: ALLOW_ACTION
  417. },
  418. "previously-certified-app": {
  419. app: DENY_ACTION,
  420. trusted: DENY_ACTION,
  421. privileged: DENY_ACTION,
  422. certified: ALLOW_ACTION
  423. }
  424. };
  425. /**
  426. * Append access modes to the permission name as suffixes.
  427. * e.g. permission name 'contacts' with ['read', 'write'] =
  428. * ['contacts-read', contacts-write']
  429. * @param string aPermName
  430. * @param array aAccess
  431. * @returns array containing access-appended permission names.
  432. **/
  433. this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) {
  434. if (aAccess.length == 0) {
  435. return [aPermName];
  436. }
  437. return aAccess.map(function(aMode) {
  438. return aPermName + "-" + aMode;
  439. });
  440. };
  441. /**
  442. * Expand an access string into multiple permission names,
  443. * e.g: permission name 'contacts' with 'readwrite' =
  444. * ['contacts-read', 'contacts-create', 'contacts-write']
  445. * @param string aPermName
  446. * @param string aAccess (optional)
  447. * @returns array containing expanded permission names.
  448. **/
  449. this.expandPermissions = function expandPermissions(aPermName, aAccess) {
  450. if (!PermissionsTable[aPermName]) {
  451. let errorMsg =
  452. "PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName;
  453. Cu.reportError(errorMsg);
  454. dump(errorMsg);
  455. return [];
  456. }
  457. const tableEntry = PermissionsTable[aPermName];
  458. if (tableEntry.substitute && tableEntry.additional) {
  459. let errorMsg =
  460. "PermissionsTable.jsm: expandPermissions: Can't handle both 'substitute' " +
  461. "and 'additional' entries for permission: " + aPermName;
  462. Cu.reportError(errorMsg);
  463. dump(errorMsg);
  464. return [];
  465. }
  466. if (!aAccess && tableEntry.access ||
  467. aAccess && !tableEntry.access) {
  468. let errorMsg =
  469. "PermissionsTable.jsm: expandPermissions: Invalid access for permission " +
  470. aPermName + ": " + aAccess + "\n";
  471. Cu.reportError(errorMsg);
  472. dump(errorMsg);
  473. return [];
  474. }
  475. let expandedPermNames = [];
  476. if (tableEntry.access && aAccess) {
  477. let requestedSuffixes = [];
  478. switch (aAccess) {
  479. case READONLY:
  480. requestedSuffixes.push("read");
  481. break;
  482. case CREATEONLY:
  483. requestedSuffixes.push("create");
  484. break;
  485. case READCREATE:
  486. requestedSuffixes.push("read", "create");
  487. break;
  488. case READWRITE:
  489. requestedSuffixes.push("read", "create", "write");
  490. break;
  491. default:
  492. return [];
  493. }
  494. let permArr = appendAccessToPermName(aPermName, requestedSuffixes);
  495. // Add the same suffix to each of the additions.
  496. if (tableEntry.additional) {
  497. for (let additional of tableEntry.additional) {
  498. permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
  499. }
  500. }
  501. // Only add the suffixed version if the suffix exists in the table.
  502. for (let idx in permArr) {
  503. let suffix = requestedSuffixes[idx % requestedSuffixes.length];
  504. if (tableEntry.access.indexOf(suffix) != -1) {
  505. expandedPermNames.push(permArr[idx]);
  506. }
  507. }
  508. } else if (tableEntry.substitute) {
  509. expandedPermNames = expandedPermNames.concat(tableEntry.substitute);
  510. } else {
  511. expandedPermNames.push(aPermName);
  512. // Include each of the additions exactly as they appear in the table.
  513. if (tableEntry.additional) {
  514. expandedPermNames = expandedPermNames.concat(tableEntry.additional);
  515. }
  516. }
  517. return expandedPermNames;
  518. };
  519. this.PermissionsReverseTable = {};
  520. this.AllPossiblePermissions = [];
  521. (function () {
  522. // PermissionsTable as it is works well for direct searches, but not
  523. // so well for reverse ones (that is, if I get something like
  524. // device-storage:music-read or indexedDB-chrome-settings-read how
  525. // do I know which permission it really is? Hence this table is
  526. // born. The idea is that
  527. // reverseTable[device-storage:music-read] should return
  528. // device-storage:music
  529. //
  530. // We also need a list of all the possible permissions for things like the
  531. // settingsmanager, so construct that while we're at it.
  532. for (let permName in PermissionsTable) {
  533. let permAliases = [];
  534. if (PermissionsTable[permName].access) {
  535. permAliases = expandPermissions(permName, "readwrite");
  536. } else if (!PermissionsTable[permName].substitute) {
  537. permAliases = expandPermissions(permName);
  538. }
  539. for (let i = 0; i < permAliases.length; i++) {
  540. PermissionsReverseTable[permAliases[i]] = permName;
  541. AllPossiblePermissions.push(permAliases[i]);
  542. }
  543. }
  544. AllPossiblePermissions =
  545. AllPossiblePermissions.concat(["indexedDB", "offline-app", "pin-app"]);
  546. })();
  547. this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {
  548. // Check to see if the 'webapp' is app/privileged/certified.
  549. let appStatus;
  550. switch (aIntStatus) {
  551. case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
  552. appStatus = "certified";
  553. break;
  554. case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
  555. appStatus = "privileged";
  556. break;
  557. default: // If it isn't certified or privileged, it's app
  558. appStatus = "app";
  559. break;
  560. }
  561. let realPerm = PermissionsReverseTable[aPermName];
  562. if (realPerm) {
  563. return (PermissionsTable[realPerm][appStatus] ==
  564. Ci.nsIPermissionManager.PROMPT_ACTION);
  565. } else {
  566. return false;
  567. }
  568. }