CheckUserPrivileges.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Get user's global privileges and some db-specific privileges
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\DatabaseInterface;
  10. use PhpMyAdmin\Util;
  11. /**
  12. * PhpMyAdmin\CheckUserPrivileges class
  13. *
  14. * @package PhpMyAdmin
  15. */
  16. class CheckUserPrivileges
  17. {
  18. /**
  19. * @var DatabaseInterface
  20. */
  21. private $dbi;
  22. /**
  23. * Constructor
  24. *
  25. * @param DatabaseInterface $dbi DatabaseInterface object
  26. */
  27. public function __construct(DatabaseInterface $dbi)
  28. {
  29. $this->dbi = $dbi;
  30. }
  31. /**
  32. * Extracts details from a result row of a SHOW GRANT query
  33. *
  34. * @param string $row grant row
  35. *
  36. * @return array
  37. */
  38. public function getItemsFromShowGrantsRow($row)
  39. {
  40. $db_name_offset = mb_strpos($row, ' ON ') + 4;
  41. $tblname_end_offset = mb_strpos($row, ' TO ');
  42. $tblname_start_offset = false;
  43. if ($__tblname_start_offset = mb_strpos($row, '`.', $db_name_offset)) {
  44. if ($__tblname_start_offset < $tblname_end_offset) {
  45. $tblname_start_offset = $__tblname_start_offset + 1;
  46. }
  47. }
  48. if (!$tblname_start_offset) {
  49. $tblname_start_offset = mb_strpos($row, '.', $db_name_offset);
  50. }
  51. $show_grants_dbname = mb_substr(
  52. $row,
  53. $db_name_offset,
  54. $tblname_start_offset - $db_name_offset
  55. );
  56. $show_grants_dbname = Util::unQuote($show_grants_dbname, '`');
  57. $show_grants_str = mb_substr(
  58. $row,
  59. 6,
  60. (mb_strpos($row, ' ON ') - 6)
  61. );
  62. $show_grants_tblname = mb_substr(
  63. $row,
  64. $tblname_start_offset + 1,
  65. $tblname_end_offset - $tblname_start_offset - 1
  66. );
  67. $show_grants_tblname = Util::unQuote($show_grants_tblname, '`');
  68. return array(
  69. $show_grants_str,
  70. $show_grants_dbname,
  71. $show_grants_tblname
  72. );
  73. }
  74. /**
  75. * Check if user has required privileges for
  76. * performing 'Adjust privileges' operations
  77. *
  78. * @param string $show_grants_str string containing grants for user
  79. * @param string $show_grants_dbname name of db extracted from grant string
  80. * @param string $show_grants_tblname name of table extracted from grant string
  81. *
  82. * @return void
  83. */
  84. public function checkRequiredPrivilegesForAdjust(
  85. $show_grants_str,
  86. $show_grants_dbname,
  87. $show_grants_tblname
  88. ) {
  89. // '... ALL PRIVILEGES ON *.* ...' OR '... ALL PRIVILEGES ON `mysql`.* ..'
  90. // OR
  91. // SELECT, INSERT, UPDATE, DELETE .... ON *.* OR `mysql`.*
  92. if ($show_grants_str == 'ALL'
  93. || $show_grants_str == 'ALL PRIVILEGES'
  94. || (mb_strpos(
  95. $show_grants_str, 'SELECT, INSERT, UPDATE, DELETE'
  96. ) !== false)
  97. ) {
  98. if ($show_grants_dbname == '*'
  99. && $show_grants_tblname == '*'
  100. ) {
  101. $GLOBALS['col_priv'] = true;
  102. $GLOBALS['db_priv'] = true;
  103. $GLOBALS['proc_priv'] = true;
  104. $GLOBALS['table_priv'] = true;
  105. if ($show_grants_str == 'ALL PRIVILEGES'
  106. || $show_grants_str == 'ALL'
  107. ) {
  108. $GLOBALS['is_reload_priv'] = true;
  109. }
  110. }
  111. // check for specific tables in `mysql` db
  112. // Ex. '... ALL PRIVILEGES on `mysql`.`columns_priv` .. '
  113. if ($show_grants_dbname == 'mysql') {
  114. switch ($show_grants_tblname) {
  115. case "columns_priv":
  116. $GLOBALS['col_priv'] = true;
  117. break;
  118. case "db":
  119. $GLOBALS['db_priv'] = true;
  120. break;
  121. case "procs_priv":
  122. $GLOBALS['proc_priv'] = true;
  123. break;
  124. case "tables_priv":
  125. $GLOBALS['table_priv'] = true;
  126. break;
  127. case "*":
  128. $GLOBALS['col_priv'] = true;
  129. $GLOBALS['db_priv'] = true;
  130. $GLOBALS['proc_priv'] = true;
  131. $GLOBALS['table_priv'] = true;
  132. break;
  133. default:
  134. }
  135. }
  136. }
  137. }
  138. /**
  139. * sets privilege information extracted from SHOW GRANTS result
  140. *
  141. * Detection for some CREATE privilege.
  142. *
  143. * Since MySQL 4.1.2, we can easily detect current user's grants using $userlink
  144. * (no control user needed) and we don't have to try any other method for
  145. * detection
  146. *
  147. * @todo fix to get really all privileges, not only explicitly defined for this user
  148. * from MySQL manual: (https://dev.mysql.com/doc/refman/5.0/en/show-grants.html)
  149. * SHOW GRANTS displays only the privileges granted explicitly to the named
  150. * account. Other privileges might be available to the account, but they are not
  151. * displayed. For example, if an anonymous account exists, the named account
  152. * might be able to use its privileges, but SHOW GRANTS will not display them.
  153. *
  154. * @return void
  155. */
  156. public function analyseShowGrant()
  157. {
  158. if (Util::cacheExists('is_create_db_priv')) {
  159. $GLOBALS['is_create_db_priv'] = Util::cacheGet(
  160. 'is_create_db_priv'
  161. );
  162. $GLOBALS['is_reload_priv'] = Util::cacheGet(
  163. 'is_reload_priv'
  164. );
  165. $GLOBALS['db_to_create'] = Util::cacheGet(
  166. 'db_to_create'
  167. );
  168. $GLOBALS['dbs_where_create_table_allowed'] = Util::cacheGet(
  169. 'dbs_where_create_table_allowed'
  170. );
  171. $GLOBALS['dbs_to_test'] = Util::cacheGet(
  172. 'dbs_to_test'
  173. );
  174. $GLOBALS['db_priv'] = Util::cacheGet(
  175. 'db_priv'
  176. );
  177. $GLOBALS['col_priv'] = Util::cacheGet(
  178. 'col_priv'
  179. );
  180. $GLOBALS['table_priv'] = Util::cacheGet(
  181. 'table_priv'
  182. );
  183. $GLOBALS['proc_priv'] = Util::cacheGet(
  184. 'proc_priv'
  185. );
  186. return;
  187. }
  188. // defaults
  189. $GLOBALS['is_create_db_priv'] = false;
  190. $GLOBALS['is_reload_priv'] = false;
  191. $GLOBALS['db_to_create'] = '';
  192. $GLOBALS['dbs_where_create_table_allowed'] = array();
  193. $GLOBALS['dbs_to_test'] = $this->dbi->getSystemSchemas();
  194. $GLOBALS['proc_priv'] = false;
  195. $GLOBALS['db_priv'] = false;
  196. $GLOBALS['col_priv'] = false;
  197. $GLOBALS['table_priv'] = false;
  198. $rs_usr = $this->dbi->tryQuery('SHOW GRANTS');
  199. if (! $rs_usr) {
  200. return;
  201. }
  202. $re0 = '(^|(\\\\\\\\)+|[^\\\\])'; // non-escaped wildcards
  203. $re1 = '(^|[^\\\\])(\\\)+'; // escaped wildcards
  204. while ($row = $this->dbi->fetchRow($rs_usr)) {
  205. list(
  206. $show_grants_str,
  207. $show_grants_dbname,
  208. $show_grants_tblname
  209. ) = $this->getItemsFromShowGrantsRow($row[0]);
  210. if ($show_grants_dbname == '*') {
  211. if ($show_grants_str != 'USAGE') {
  212. $GLOBALS['dbs_to_test'] = false;
  213. }
  214. } elseif ($GLOBALS['dbs_to_test'] !== false) {
  215. $GLOBALS['dbs_to_test'][] = $show_grants_dbname;
  216. }
  217. if (
  218. mb_strpos($show_grants_str,'RELOAD') !== false
  219. ) {
  220. $GLOBALS['is_reload_priv'] = true;
  221. }
  222. // check for the required privileges for adjust
  223. $this->checkRequiredPrivilegesForAdjust(
  224. $show_grants_str,
  225. $show_grants_dbname,
  226. $show_grants_tblname
  227. );
  228. /**
  229. * @todo if we find CREATE VIEW but not CREATE, do not offer
  230. * the create database dialog box
  231. */
  232. if ($show_grants_str == 'ALL'
  233. || $show_grants_str == 'ALL PRIVILEGES'
  234. || $show_grants_str == 'CREATE'
  235. || strpos($show_grants_str, 'CREATE,') !== false
  236. ) {
  237. if ($show_grants_dbname == '*') {
  238. // a global CREATE privilege
  239. $GLOBALS['is_create_db_priv'] = true;
  240. $GLOBALS['is_reload_priv'] = true;
  241. $GLOBALS['db_to_create'] = '';
  242. $GLOBALS['dbs_where_create_table_allowed'][] = '*';
  243. // @todo we should not break here, cause GRANT ALL *.*
  244. // could be revoked by a later rule like GRANT SELECT ON db.*
  245. break;
  246. } else {
  247. // this array may contain wildcards
  248. $GLOBALS['dbs_where_create_table_allowed'][] = $show_grants_dbname;
  249. $dbname_to_test = Util::backquote($show_grants_dbname);
  250. if ($GLOBALS['is_create_db_priv']) {
  251. // no need for any more tests if we already know this
  252. continue;
  253. }
  254. // does this db exist?
  255. if ((preg_match('/' . $re0 . '%|_/', $show_grants_dbname)
  256. && ! preg_match('/\\\\%|\\\\_/', $show_grants_dbname))
  257. || (! $this->dbi->tryQuery(
  258. 'USE ' . preg_replace(
  259. '/' . $re1 . '(%|_)/', '\\1\\3', $dbname_to_test
  260. )
  261. )
  262. && mb_substr($this->dbi->getError(), 1, 4) != 1044)
  263. ) {
  264. /**
  265. * Do not handle the underscore wildcard
  266. * (this case must be rare anyway)
  267. */
  268. $GLOBALS['db_to_create'] = preg_replace(
  269. '/' . $re0 . '%/', '\\1',
  270. $show_grants_dbname
  271. );
  272. $GLOBALS['db_to_create'] = preg_replace(
  273. '/' . $re1 . '(%|_)/', '\\1\\3',
  274. $GLOBALS['db_to_create']
  275. );
  276. $GLOBALS['is_create_db_priv'] = true;
  277. /**
  278. * @todo collect $GLOBALS['db_to_create'] into an array,
  279. * to display a drop-down in the "Create database" dialog
  280. */
  281. // we don't break, we want all possible databases
  282. //break;
  283. } // end if
  284. } // end elseif
  285. } // end if
  286. } // end while
  287. $this->dbi->freeResult($rs_usr);
  288. // must also cacheUnset() them in
  289. // PhpMyAdmin\Plugins\Auth\AuthenticationCookie
  290. Util::cacheSet('is_create_db_priv', $GLOBALS['is_create_db_priv']);
  291. Util::cacheSet('is_reload_priv', $GLOBALS['is_reload_priv']);
  292. Util::cacheSet('db_to_create', $GLOBALS['db_to_create']);
  293. Util::cacheSet(
  294. 'dbs_where_create_table_allowed',
  295. $GLOBALS['dbs_where_create_table_allowed']
  296. );
  297. Util::cacheSet('dbs_to_test', $GLOBALS['dbs_to_test']);
  298. Util::cacheSet('proc_priv', $GLOBALS['proc_priv']);
  299. Util::cacheSet('table_priv', $GLOBALS['table_priv']);
  300. Util::cacheSet('col_priv', $GLOBALS['col_priv']);
  301. Util::cacheSet('db_priv', $GLOBALS['db_priv']);
  302. } // end function
  303. }