3 * Misc stuff and REQUIRED by ALL the scripts.
4 * MUST be included by every script
6 * Among other things, it contains the advanced authentication work.
8 * Order of sections for common.inc.php:
10 * the authentication libraries must be before the connection to db
12 * ... so the required order is:
14 * LABEL_variables_init
15 * - initialize some variables always needed
16 * LABEL_parsing_config_file
17 * - parsing of the configuration file
18 * LABEL_loading_language_file
19 * - loading language file
21 * - check and setup configured servers
25 * - load of MySQL extension (if necessary)
26 * - loading of an authentication library
28 * - authentication work
30 declare(strict_types
=1);
32 use PhpMyAdmin\Config
;
34 use PhpMyAdmin\DatabaseInterface
;
35 use PhpMyAdmin\Di\Migration
;
36 use PhpMyAdmin\ErrorHandler
;
37 use PhpMyAdmin\LanguageManager
;
38 use PhpMyAdmin\Logging
;
39 use PhpMyAdmin\Message
;
40 use PhpMyAdmin\MoTranslator\Loader
;
41 use PhpMyAdmin\Plugins\AuthenticationPlugin
;
42 use PhpMyAdmin\Response
;
43 use PhpMyAdmin\Routing
;
44 use PhpMyAdmin\Sanitize
;
45 use PhpMyAdmin\Session
;
46 use PhpMyAdmin\SqlParser\Lexer
;
47 use PhpMyAdmin\ThemeManager
;
48 use PhpMyAdmin\Tracker
;
50 use Symfony\Component\Config\FileLocator
;
51 use Symfony\Component\DependencyInjection\ContainerBuilder
;
52 use Symfony\Component\DependencyInjection\Loader\PhpFileLoader
;
54 global $containerBuilder, $error_handler, $PMA_Config, $server, $dbi, $lang, $cfg, $isConfigLoading, $auth_plugin, $route;
57 * block attempts to directly run this script
59 if (getcwd() == __DIR__
) {
60 die('Attack stopped');
64 * Minimum PHP version; can't call Core::fatalError() which uses a
65 * PHP 5 function, so cannot easily localize this message.
67 if (PHP_VERSION_ID
< 70103) {
69 '<p>PHP 7.1.3+ is required.</p>'
70 . '<p>Currently installed version is: ' . PHP_VERSION
. '</p>'
74 // phpcs:disable PSR1.Files.SideEffects
76 * for verification in all procedural scripts under libraries
78 define('PHPMYADMIN', true);
82 * Load vendor configuration.
84 require_once ROOT_PATH
. 'libraries/vendor_config.php';
89 if (! @is_readable
(AUTOLOAD_FILE
)) {
91 '<p>File <samp>' . AUTOLOAD_FILE
. '</samp> missing or not readable.</p>'
92 . '<p>Most likely you did not run Composer to '
93 . '<a href="https://docs.phpmyadmin.net/en/latest/setup.html#installing-from-git">'
94 . 'install library files</a>.</p>'
97 require_once AUTOLOAD_FILE
;
99 $route = Routing
::getCurrentRoute();
101 if ($route === '/import-status') {
102 // phpcs:disable PSR1.Files.SideEffects
103 define('PMA_MINIMUM_COMMON', true);
107 $containerBuilder = new ContainerBuilder();
108 $loader = new PhpFileLoader($containerBuilder, new FileLocator(__DIR__
));
109 $loader->load('services_loader.php');
110 /** @var Migration $diMigration */
111 $diMigration = $containerBuilder->get('di_migration');
114 * Load gettext functions.
116 Loader
::loadFunctions();
118 /** @var ErrorHandler $error_handler */
119 $error_handler = $containerBuilder->get('error_handler');
122 * Warning about missing PHP extensions.
124 Core
::checkExtensions();
127 * Configure required PHP settings.
131 /* start procedural code label_start_procedural */
133 Core
::cleanupPathInfo();
135 /* parsing configuration file LABEL_parsing_config_file */
137 /** @var bool $isConfigLoading Indication for the error handler */
138 $isConfigLoading = false;
141 * Force reading of config file, because we removed sensitive values
142 * in the previous iteration.
144 * @var Config $PMA_Config
146 $PMA_Config = $containerBuilder->get('config');
148 register_shutdown_function([Config
::class, 'fatalErrorHandler']);
151 * include session handling after the globals, to prevent overwriting
153 if (! defined('PMA_NO_SESSION')) {
154 Session
::setUp($PMA_Config, $error_handler);
158 * init some variables LABEL_variables_init
162 * holds parameters to be passed to next page
164 * @global array $url_params
166 $diMigration->setGlobal('url_params', []);
169 * holds page that should be displayed
171 * @global string $goto
173 $diMigration->setGlobal('goto', '');
174 // Security fix: disallow accessing serious server files via "?goto="
175 if (isset($_REQUEST['goto']) && Core
::checkPageValidity($_REQUEST['goto'])) {
176 $diMigration->setGlobal('goto', $_REQUEST['goto']);
177 $diMigration->setGlobal('url_params', ['goto' => $_REQUEST['goto']]);
179 $PMA_Config->removeCookie('goto');
180 unset($_REQUEST['goto'], $_GET['goto'], $_POST['goto']);
186 * @global string $back
188 if (isset($_REQUEST['back']) && Core
::checkPageValidity($_REQUEST['back'])) {
189 $diMigration->setGlobal('back', $_REQUEST['back']);
191 $PMA_Config->removeCookie('back');
192 unset($_REQUEST['back'], $_GET['back'], $_POST['back']);
196 * Check whether user supplied token is valid, if not remove any possibly
197 * dangerous stuff from request.
199 * remember that some objects in the session with session_start and __wakeup()
200 * could access this variables before we reach this point
201 * f.e. PhpMyAdmin\Config: fontsize
203 * Check for token mismatch only if the Request method is POST
204 * GET Requests would never have token and therefore checking
205 * mis-match does not make sense
207 * @todo variables should be handled by their respective owners (objects)
208 * f.e. lang, server in PhpMyAdmin\Config
211 $token_mismatch = true;
212 $token_provided = false;
214 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
215 if (Core
::isValid($_POST['token'])) {
216 $token_provided = true;
217 $token_mismatch = ! @hash_equals
($_SESSION[' PMA_token '], $_POST['token']);
220 if ($token_mismatch) {
221 /* Warn in case the mismatch is result of failed setting of session cookie */
222 if (isset($_POST['set_session']) && $_POST['set_session'] != session_id()) {
225 'Failed to set session cookie. Maybe you are using '
226 . 'HTTP instead of HTTPS to access phpMyAdmin.'
232 * We don't allow any POST operation parameters if the token is mismatched
235 $whitelist = ['ajax_request'];
236 Sanitize
::removeRequestVars($whitelist);
242 * current selected database
246 Core
::setGlobalDbOrTable('db');
249 * current selected table
251 * @global string $table
253 Core
::setGlobalDbOrTable('table');
256 * Store currently selected recent table.
257 * Affect $db and $table globals
259 if (isset($_REQUEST['selected_recent_table']) && Core
::isValid($_REQUEST['selected_recent_table'])) {
260 $recent_table = json_decode($_REQUEST['selected_recent_table'], true);
262 $diMigration->setGlobal(
264 array_key_exists('db', $recent_table) && is_string($recent_table['db']) ?
$recent_table['db'] : ''
266 $diMigration->setGlobal(
268 ['db' => $containerBuilder->getParameter('db')] +
$containerBuilder->getParameter('url_params')
271 $diMigration->setGlobal(
273 array_key_exists('table', $recent_table) && is_string($recent_table['table']) ?
$recent_table['table'] : ''
275 $diMigration->setGlobal(
277 ['table' => $containerBuilder->getParameter('table')] +
$containerBuilder->getParameter('url_params')
282 * SQL query to be executed
284 * @global string $sql_query
286 $diMigration->setGlobal('sql_query', '');
287 if (Core
::isValid($_POST['sql_query'])) {
288 $diMigration->setGlobal('sql_query', $_POST['sql_query']);
291 //$_REQUEST['set_theme'] // checked later in this file LABEL_theme_setup
292 //$_REQUEST['server']; // checked later in this file
293 //$_REQUEST['lang']; // checked by LABEL_loading_language_file
295 /* loading language file LABEL_loading_language_file */
298 * lang detection is done here
300 $language = LanguageManager
::getInstance()->selectLanguage();
301 $language->activate();
304 * check for errors occurred while loading configuration
305 * this check is done here after loading language files to present errors in locale
307 $PMA_Config->checkPermissions();
308 $PMA_Config->checkErrors();
310 /* Check server configuration */
311 Core
::checkConfiguration();
313 /* Check request for possible attacks */
314 Core
::checkRequest();
316 /* setup servers LABEL_setup_servers */
318 $PMA_Config->checkServers();
323 * @global integer $server
325 $diMigration->setGlobal('server', $PMA_Config->selectServer());
326 $diMigration->setGlobal('url_params', ['server' => $containerBuilder->getParameter('server')] +
$containerBuilder->getParameter('url_params'));
329 * BC - enable backward compatibility
330 * exports all configuration settings into globals ($cfg global)
332 $PMA_Config->enableBc();
334 /* setup themes LABEL_theme_setup */
336 ThemeManager
::initializeTheme();
338 /** @var DatabaseInterface $dbi */
341 if (! defined('PMA_MINIMUM_COMMON')) {
343 * save some settings in cookies
345 * @todo should be done in PhpMyAdmin\Config
347 $PMA_Config->setCookie('pma_lang', $lang);
349 ThemeManager
::getInstance()->setThemeCookie();
351 $containerBuilder->set(DatabaseInterface
::class, DatabaseInterface
::load());
352 $containerBuilder->setAlias('dbi', DatabaseInterface
::class);
354 if (! empty($cfg['Server'])) {
355 // get LoginCookieValidity from preferences cache
356 // no generic solution for loading preferences from cache as some settings
357 // need to be kept for processing in
358 // PhpMyAdmin\Config::loadUserPreferences()
359 $cache_key = 'server_' . $server;
360 if (isset($_SESSION['cache'][$cache_key]['userprefs']['LoginCookieValidity'])
363 = $_SESSION['cache'][$cache_key]['userprefs']['LoginCookieValidity'];
364 $PMA_Config->set('LoginCookieValidity', $value);
365 $cfg['LoginCookieValidity'] = $value;
370 // Gets the authentication library that fits the $cfg['Server'] settings
371 // and run authentication
374 * the required auth type plugin
376 $auth_class = 'PhpMyAdmin\\Plugins\\Auth\\Authentication' . ucfirst(strtolower($cfg['Server']['auth_type']));
377 if (! @class_exists
($auth_class)) {
379 __('Invalid authentication method set in configuration:')
380 . ' ' . $cfg['Server']['auth_type']
383 if (isset($_POST['pma_password']) && strlen($_POST['pma_password']) > 256) {
384 $_POST['pma_password'] = substr($_POST['pma_password'], 0, 256);
387 /** @var AuthenticationPlugin $auth_plugin */
388 $auth_plugin = new $auth_class();
389 $auth_plugin->authenticate();
391 // Try to connect MySQL with the control user profile (will be used to
392 // get the privileges list for the current user but the true user link
393 // must be open after this one so it would be default one for all the
395 $controllink = false;
396 if ($cfg['Server']['controluser'] != '') {
397 $controllink = $dbi->connect(
398 DatabaseInterface
::CONNECT_CONTROL
402 // Connects to the server (validates user's login)
403 /** @var DatabaseInterface $userlink */
404 $userlink = $dbi->connect(DatabaseInterface
::CONNECT_USER
);
406 if ($userlink === false) {
407 $auth_plugin->showFailure('mysql-denied');
410 if (! $controllink) {
412 * Open separate connection for control queries, this is needed
413 * to avoid problems with table locking used in main connection
414 * and phpMyAdmin issuing queries to configuration storage, which
415 * is not locked by that time.
417 $controllink = $dbi->connect(
418 DatabaseInterface
::CONNECT_USER
,
420 DatabaseInterface
::CONNECT_CONTROL
424 $auth_plugin->rememberCredentials();
426 $auth_plugin->checkTwoFactor();
429 Logging
::logUser($cfg['Server']['user']);
431 if ($dbi->getVersion() < $cfg['MysqlMinVersion']['internal']) {
433 __('You should upgrade to %s %s or later.'),
436 $cfg['MysqlMinVersion']['human'],
441 // Sets the default delimiter (if specified).
442 if (! empty($_REQUEST['sql_delimiter'])) {
443 Lexer
::$DEFAULT_DELIMITER = $_REQUEST['sql_delimiter'];
446 // TODO: Set SQL modes too.
447 } else { // end server connecting
448 $response = Response
::getInstance();
449 $response->getHeader()->disableMenuAndConsole();
450 $response->getFooter()->setMinimal();
454 * check if profiling was requested and remember it
455 * (note: when $cfg['ServerDefault'] = 0, constant is not defined)
457 if (isset($_REQUEST['profiling'])
458 && Util
::profilingSupported()
460 $_SESSION['profiling'] = true;
461 } elseif (isset($_REQUEST['profiling_form'])) {
462 // the checkbox was unchecked
463 unset($_SESSION['profiling']);
467 * Inclusion of profiling scripts is needed on various
468 * pages like sql, tbl_sql, db_sql, tbl_select
470 $response = Response
::getInstance();
471 if (isset($_SESSION['profiling'])) {
472 $scripts = $response->getHeader()->getScripts();
473 $scripts->addFile('chart.js');
474 $scripts->addFile('vendor/jqplot/jquery.jqplot.js');
475 $scripts->addFile('vendor/jqplot/plugins/jqplot.pieRenderer.js');
476 $scripts->addFile('vendor/jqplot/plugins/jqplot.highlighter.js');
477 $scripts->addFile('vendor/jquery/jquery.tablesorter.js');
481 * There is no point in even attempting to process
482 * an ajax request if there is a token mismatch
484 if ($response->isAjax() && $_SERVER['REQUEST_METHOD'] == 'POST' && $token_mismatch) {
485 $response->setRequestStatus(false);
488 Message
::error(__('Error: Token mismatch'))
493 $containerBuilder->set('response', Response
::getInstance());
496 // load user preferences
497 $PMA_Config->loadUserPreferences();
499 $containerBuilder->set('theme_manager', ThemeManager
::getInstance());
501 /* Tell tracker that it can actually work */
504 if (! defined('PMA_MINIMUM_COMMON')
505 && ! empty($GLOBALS['server'])
506 && isset($GLOBALS['cfg']['ZeroConf'])
507 && $GLOBALS['cfg']['ZeroConf'] == true
509 $GLOBALS['dbi']->postConnectControl();