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