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\Sanitize
;
44 use PhpMyAdmin\Session
;
45 use PhpMyAdmin\SqlParser\Lexer
;
46 use PhpMyAdmin\ThemeManager
;
47 use PhpMyAdmin\Tracker
;
49 use Symfony\Component\Config\FileLocator
;
50 use Symfony\Component\DependencyInjection\ContainerBuilder
;
51 use Symfony\Component\DependencyInjection\Loader\PhpFileLoader
;
53 global $containerBuilder, $error_handler, $PMA_Config, $server, $dbi, $lang, $cfg, $isConfigLoading, $auth_plugin;
56 * block attempts to directly run this script
58 if (getcwd() == __DIR__
) {
59 die('Attack stopped');
63 * Minimum PHP version; can't call Core::fatalError() which uses a
64 * PHP 5 function, so cannot easily localize this message.
66 if (PHP_VERSION_ID
< 70103) {
68 '<p>PHP 7.1.3+ is required.</p>'
69 . '<p>Currently installed version is: ' . PHP_VERSION
. '</p>'
73 // phpcs:disable PSR1.Files.SideEffects
75 * for verification in all procedural scripts under libraries
77 define('PHPMYADMIN', true);
81 * Load vendor configuration.
83 require_once ROOT_PATH
. 'libraries/vendor_config.php';
88 if (! @is_readable
(AUTOLOAD_FILE
)) {
90 '<p>File <samp>' . AUTOLOAD_FILE
. '</samp> missing or not readable.</p>'
91 . '<p>Most likely you did not run Composer to '
92 . '<a href="https://docs.phpmyadmin.net/en/latest/setup.html#installing-from-git">'
93 . 'install library files</a>.</p>'
96 require_once AUTOLOAD_FILE
;
98 $containerBuilder = new ContainerBuilder();
99 $loader = new PhpFileLoader($containerBuilder, new FileLocator(__DIR__
));
100 $loader->load('services_loader.php');
101 /** @var Migration $diMigration */
102 $diMigration = $containerBuilder->get('di_migration');
105 * Load gettext functions.
107 Loader
::loadFunctions();
109 /** @var ErrorHandler $error_handler */
110 $error_handler = $containerBuilder->get('error_handler');
113 * Warning about missing PHP extensions.
115 Core
::checkExtensions();
118 * Configure required PHP settings.
122 /* start procedural code label_start_procedural */
124 Core
::cleanupPathInfo();
126 /* parsing configuration file LABEL_parsing_config_file */
128 /** @var bool $isConfigLoading Indication for the error handler */
129 $isConfigLoading = false;
132 * Force reading of config file, because we removed sensitive values
133 * in the previous iteration.
135 * @var Config $PMA_Config
137 $PMA_Config = $containerBuilder->get('config');
139 register_shutdown_function([Config
::class, 'fatalErrorHandler']);
142 * include session handling after the globals, to prevent overwriting
144 if (! defined('PMA_NO_SESSION')) {
145 Session
::setUp($PMA_Config, $error_handler);
149 * init some variables LABEL_variables_init
153 * holds parameters to be passed to next page
155 * @global array $url_params
157 $diMigration->setGlobal('url_params', []);
160 * holds page that should be displayed
162 * @global string $goto
164 $diMigration->setGlobal('goto', '');
165 // Security fix: disallow accessing serious server files via "?goto="
166 if (isset($_REQUEST['goto']) && Core
::checkPageValidity($_REQUEST['goto'])) {
167 $diMigration->setGlobal('goto', $_REQUEST['goto']);
168 $diMigration->setGlobal('url_params', ['goto' => $_REQUEST['goto']]);
170 $PMA_Config->removeCookie('goto');
171 unset($_REQUEST['goto'], $_GET['goto'], $_POST['goto']);
177 * @global string $back
179 if (isset($_REQUEST['back']) && Core
::checkPageValidity($_REQUEST['back'])) {
180 $diMigration->setGlobal('back', $_REQUEST['back']);
182 $PMA_Config->removeCookie('back');
183 unset($_REQUEST['back'], $_GET['back'], $_POST['back']);
187 * Check whether user supplied token is valid, if not remove any possibly
188 * dangerous stuff from request.
190 * remember that some objects in the session with session_start and __wakeup()
191 * could access this variables before we reach this point
192 * f.e. PhpMyAdmin\Config: fontsize
194 * Check for token mismatch only if the Request method is POST
195 * GET Requests would never have token and therefore checking
196 * mis-match does not make sense
198 * @todo variables should be handled by their respective owners (objects)
199 * f.e. lang, server in PhpMyAdmin\Config
202 $token_mismatch = true;
203 $token_provided = false;
205 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
206 if (Core
::isValid($_POST['token'])) {
207 $token_provided = true;
208 $token_mismatch = ! @hash_equals
($_SESSION[' PMA_token '], $_POST['token']);
211 if ($token_mismatch) {
212 /* Warn in case the mismatch is result of failed setting of session cookie */
213 if (isset($_POST['set_session']) && $_POST['set_session'] != session_id()) {
216 'Failed to set session cookie. Maybe you are using '
217 . 'HTTP instead of HTTPS to access phpMyAdmin.'
223 * We don't allow any POST operation parameters if the token is mismatched
226 $whitelist = ['ajax_request'];
227 Sanitize
::removeRequestVars($whitelist);
233 * current selected database
237 Core
::setGlobalDbOrTable('db');
240 * current selected table
242 * @global string $table
244 Core
::setGlobalDbOrTable('table');
247 * Store currently selected recent table.
248 * Affect $db and $table globals
250 if (isset($_REQUEST['selected_recent_table']) && Core
::isValid($_REQUEST['selected_recent_table'])) {
251 $recent_table = json_decode($_REQUEST['selected_recent_table'], true);
253 $diMigration->setGlobal(
255 array_key_exists('db', $recent_table) && is_string($recent_table['db']) ?
$recent_table['db'] : ''
257 $diMigration->setGlobal(
259 ['db' => $containerBuilder->getParameter('db')] +
$containerBuilder->getParameter('url_params')
262 $diMigration->setGlobal(
264 array_key_exists('table', $recent_table) && is_string($recent_table['table']) ?
$recent_table['table'] : ''
266 $diMigration->setGlobal(
268 ['table' => $containerBuilder->getParameter('table')] +
$containerBuilder->getParameter('url_params')
273 * SQL query to be executed
275 * @global string $sql_query
277 $diMigration->setGlobal('sql_query', '');
278 if (Core
::isValid($_POST['sql_query'])) {
279 $diMigration->setGlobal('sql_query', $_POST['sql_query']);
282 //$_REQUEST['set_theme'] // checked later in this file LABEL_theme_setup
283 //$_REQUEST['server']; // checked later in this file
284 //$_REQUEST['lang']; // checked by LABEL_loading_language_file
286 /* loading language file LABEL_loading_language_file */
289 * lang detection is done here
291 $language = LanguageManager
::getInstance()->selectLanguage();
292 $language->activate();
295 * check for errors occurred while loading configuration
296 * this check is done here after loading language files to present errors in locale
298 $PMA_Config->checkPermissions();
299 $PMA_Config->checkErrors();
301 /* Check server configuration */
302 Core
::checkConfiguration();
304 /* Check request for possible attacks */
305 Core
::checkRequest();
307 /* setup servers LABEL_setup_servers */
309 $PMA_Config->checkServers();
314 * @global integer $server
316 $diMigration->setGlobal('server', $PMA_Config->selectServer());
317 $diMigration->setGlobal('url_params', ['server' => $containerBuilder->getParameter('server')] +
$containerBuilder->getParameter('url_params'));
320 * BC - enable backward compatibility
321 * exports all configuration settings into globals ($cfg global)
323 $PMA_Config->enableBc();
325 /* setup themes LABEL_theme_setup */
327 ThemeManager
::initializeTheme();
329 /** @var DatabaseInterface $dbi */
332 if (! defined('PMA_MINIMUM_COMMON')) {
334 * save some settings in cookies
336 * @todo should be done in PhpMyAdmin\Config
338 $PMA_Config->setCookie('pma_lang', $lang);
340 ThemeManager
::getInstance()->setThemeCookie();
342 $containerBuilder->set(DatabaseInterface
::class, DatabaseInterface
::load());
343 $containerBuilder->setAlias('dbi', DatabaseInterface
::class);
345 if (! empty($cfg['Server'])) {
346 // get LoginCookieValidity from preferences cache
347 // no generic solution for loading preferences from cache as some settings
348 // need to be kept for processing in
349 // PhpMyAdmin\Config::loadUserPreferences()
350 $cache_key = 'server_' . $server;
351 if (isset($_SESSION['cache'][$cache_key]['userprefs']['LoginCookieValidity'])
354 = $_SESSION['cache'][$cache_key]['userprefs']['LoginCookieValidity'];
355 $PMA_Config->set('LoginCookieValidity', $value);
356 $cfg['LoginCookieValidity'] = $value;
361 // Gets the authentication library that fits the $cfg['Server'] settings
362 // and run authentication
365 * the required auth type plugin
367 $auth_class = 'PhpMyAdmin\\Plugins\\Auth\\Authentication' . ucfirst(strtolower($cfg['Server']['auth_type']));
368 if (! @class_exists
($auth_class)) {
370 __('Invalid authentication method set in configuration:')
371 . ' ' . $cfg['Server']['auth_type']
374 if (isset($_POST['pma_password']) && strlen($_POST['pma_password']) > 256) {
375 $_POST['pma_password'] = substr($_POST['pma_password'], 0, 256);
378 /** @var AuthenticationPlugin $auth_plugin */
379 $auth_plugin = new $auth_class();
380 $auth_plugin->authenticate();
382 // Try to connect MySQL with the control user profile (will be used to
383 // get the privileges list for the current user but the true user link
384 // must be open after this one so it would be default one for all the
386 $controllink = false;
387 if ($cfg['Server']['controluser'] != '') {
388 $controllink = $dbi->connect(
389 DatabaseInterface
::CONNECT_CONTROL
393 // Connects to the server (validates user's login)
394 /** @var DatabaseInterface $userlink */
395 $userlink = $dbi->connect(DatabaseInterface
::CONNECT_USER
);
397 if ($userlink === false) {
398 $auth_plugin->showFailure('mysql-denied');
401 if (! $controllink) {
403 * Open separate connection for control queries, this is needed
404 * to avoid problems with table locking used in main connection
405 * and phpMyAdmin issuing queries to configuration storage, which
406 * is not locked by that time.
408 $controllink = $dbi->connect(
409 DatabaseInterface
::CONNECT_USER
,
411 DatabaseInterface
::CONNECT_CONTROL
415 $auth_plugin->rememberCredentials();
417 $auth_plugin->checkTwoFactor();
420 Logging
::logUser($cfg['Server']['user']);
422 if ($dbi->getVersion() < $cfg['MysqlMinVersion']['internal']) {
424 __('You should upgrade to %s %s or later.'),
427 $cfg['MysqlMinVersion']['human'],
432 // Sets the default delimiter (if specified).
433 if (! empty($_REQUEST['sql_delimiter'])) {
434 Lexer
::$DEFAULT_DELIMITER = $_REQUEST['sql_delimiter'];
437 // TODO: Set SQL modes too.
438 } else { // end server connecting
439 $response = Response
::getInstance();
440 $response->getHeader()->disableMenuAndConsole();
441 $response->getFooter()->setMinimal();
445 * check if profiling was requested and remember it
446 * (note: when $cfg['ServerDefault'] = 0, constant is not defined)
448 if (isset($_REQUEST['profiling'])
449 && Util
::profilingSupported()
451 $_SESSION['profiling'] = true;
452 } elseif (isset($_REQUEST['profiling_form'])) {
453 // the checkbox was unchecked
454 unset($_SESSION['profiling']);
458 * Inclusion of profiling scripts is needed on various
459 * pages like sql, tbl_sql, db_sql, tbl_select
461 $response = Response
::getInstance();
462 if (isset($_SESSION['profiling'])) {
463 $scripts = $response->getHeader()->getScripts();
464 $scripts->addFile('chart.js');
465 $scripts->addFile('vendor/jqplot/jquery.jqplot.js');
466 $scripts->addFile('vendor/jqplot/plugins/jqplot.pieRenderer.js');
467 $scripts->addFile('vendor/jqplot/plugins/jqplot.highlighter.js');
468 $scripts->addFile('vendor/jquery/jquery.tablesorter.js');
472 * There is no point in even attempting to process
473 * an ajax request if there is a token mismatch
475 if ($response->isAjax() && $_SERVER['REQUEST_METHOD'] == 'POST' && $token_mismatch) {
476 $response->setRequestStatus(false);
479 Message
::error(__('Error: Token mismatch'))
484 $containerBuilder->set('response', Response
::getInstance());
487 // load user preferences
488 $PMA_Config->loadUserPreferences();
490 $containerBuilder->set('theme_manager', ThemeManager
::getInstance());
492 /* Tell tracker that it can actually work */
495 if (! defined('PMA_MINIMUM_COMMON')
496 && ! empty($GLOBALS['server'])
497 && isset($GLOBALS['cfg']['ZeroConf'])
498 && $GLOBALS['cfg']['ZeroConf'] == true
500 $GLOBALS['dbi']->postConnectControl();