CheckUserPrivileges.php 11 KB

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