From c91e80b6ca98aed4eaa8ab5fda006c3f4c54d890 Mon Sep 17 00:00:00 2001 From: Alex Marin Date: Fri, 22 Jun 2012 10:10:05 +0300 Subject: [PATCH] oop: AuthenticationCookie --- libraries/blowfish.php | 54 -- .../auth/AuthenticationCookie.class.php | 616 +++++++++++++++++- .../HordeCipherBlowfishOperations.class.php | 67 ++ 3 files changed, 675 insertions(+), 62 deletions(-) create mode 100644 libraries/plugins/auth/HordeCipherBlowfishOperations.class.php diff --git a/libraries/blowfish.php b/libraries/blowfish.php index 82c67331eb..7c297ec701 100644 --- a/libraries/blowfish.php +++ b/libraries/blowfish.php @@ -478,59 +478,5 @@ class Horde_Cipher_blowfish return pack("NN", $R ^ $this->p[0], $L); } - } - -// higher-level functions: -/** - * Encryption using blowfish algorithm - * - * @param string $data original data - * @param string $secret the secret - * - * @return string the encrypted result - * - * @access public - * - */ -function PMA_blowfish_encrypt($data, $secret) -{ - $pma_cipher = new Horde_Cipher_blowfish; - $encrypt = ''; - - $mod = strlen($data) % 8; - - if ($mod > 0) { - $data .= str_repeat("\0", 8 - $mod); - } - - foreach (str_split($data, 8) as $chunk) { - $encrypt .= $pma_cipher->encryptBlock($chunk, $secret); - } - return base64_encode($encrypt); -} - -/** - * Decryption using blowfish algorithm - * - * @param string $encdata encrypted data - * @param string $secret the secret - * - * @return string original data - * - * @access public - * - */ -function PMA_blowfish_decrypt($encdata, $secret) -{ - $pma_cipher = new Horde_Cipher_blowfish; - $decrypt = ''; - $data = base64_decode($encdata); - - foreach (str_split($data, 8) as $chunk) { - $decrypt .= $pma_cipher->decryptBlock($chunk, $secret); - } - return trim($decrypt); -} - ?> diff --git a/libraries/plugins/auth/AuthenticationCookie.class.php b/libraries/plugins/auth/AuthenticationCookie.class.php index 2733135bea..9c15c77ca9 100644 --- a/libraries/plugins/auth/AuthenticationCookie.class.php +++ b/libraries/plugins/auth/AuthenticationCookie.class.php @@ -13,6 +13,19 @@ if (! defined('PHPMYADMIN')) { /* Get the authentication interface */ require_once "libraries/plugins/AuthenticationPlugin.class.php"; +/** + * Remember where to redirect the user + * in case of an expired session. + */ +if (! empty($_REQUEST['target'])) { + $GLOBALS['target'] = $_REQUEST['target']; +} + +/** + * Swekey authentication functions. + */ +require './libraries/plugins/auth/swekey/swekey.auth.lib.php'; + /** * Handles the cookie authentication method * @@ -21,57 +34,618 @@ require_once "libraries/plugins/AuthenticationPlugin.class.php"; class AuthenticationCookie extends AuthenticationPlugin { /** + * Initialization vector * + * @var array + */ + private $_iv; + + /** + * Constructor + */ + function __construct() { + /** + * Initialization + * Store the initialization vector because it will be needed for + * further decryption. I don't think necessary to have one iv + * per server so I don't put the server number in the cookie name. + */ + if (empty($_COOKIE['pma_mcrypt_iv']) + || false === ($this->_setIv(base64_decode($_COOKIE['pma_mcrypt_iv'], true))) + ) { + srand((double) microtime() * 1000000); + $td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, ''); + if ($td === false) { + PMA_fatalError(__('Failed to use Blowfish from mcrypt!')); + } + $this->_setIv(mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND)); + $GLOBALS['PMA_Config']->setCookie('pma_mcrypt_iv', base64_encode($this->_getIv())); + } + } + + /** + * Displays authentication form + * + * this function MUST exit/quit the application + * + * @global string the last connection error * * @return void */ public function auth() { + global $conn_error; + + $response = PMA_Response::getInstance(); + if ($response->isAjax()) { + $response->isSuccess(false); + if (! empty($conn_error)) { + $response->addJSON('message', $conn_error); + } else { + $response->addJSON('message', PMA_Message::error(__('Your session has expired. Please login again.'))); + } + exit; + } + + /* Perform logout to custom URL */ + if (! empty($_REQUEST['old_usr']) + && ! empty($GLOBALS['cfg']['Server']['LogoutURL']) + ) { + PMA_sendHeaderLocation($GLOBALS['cfg']['Server']['LogoutURL']); + exit; + } + + /* No recall if blowfish secret is not configured as it would produce garbage */ + if ($GLOBALS['cfg']['LoginCookieRecall'] + && ! empty($GLOBALS['cfg']['blowfish_secret']) + ) { + $default_user = $GLOBALS['PHP_AUTH_USER']; + $default_server = $GLOBALS['pma_auth_server']; + $autocomplete = ''; + } else { + $default_user = ''; + $default_server = ''; + // skip the IE autocomplete feature. + $autocomplete = ' autocomplete="off"'; + } + + $cell_align = ($GLOBALS['text_dir'] == 'ltr') ? 'left' : 'right'; + + $response->getFooter()->setMinimal(); + $header = $response->getHeader(); + $header->setBodyId('loginform'); + $header->setTitle('phpMyAdmin'); + $header->disableMenu(); + $header->disableWarnings(); + + if (file_exists(CUSTOM_HEADER_FILE)) { + include CUSTOM_HEADER_FILE; + } + ?> + +
+ +

+ phpMyAdmin' + ); + ?> +

+ display(); + } + + echo "\n"; + + echo "
"; + // Displays the languages form + if (empty($GLOBALS['cfg']['Lang'])) { + include_once './libraries/display_select_lang.lib.php'; + // use fieldset, don't show doc link + PMA_select_language(true, false); + } + echo "
"; + + ?> +
+ +
target="_top" class="login hide js-show"> +
+ + + + + +
+ + +
+ +
+ + +
+
+ + +
+ 1) { + ?> +
+ +
'; + } else { + echo ' '; + } // end if (server choice) + ?> +
+
+ + +
+
+ + hasDisplayErrors()) { + echo '
'; + $GLOBALS['error_handler']->dispErrors(); + echo '
'; + } + ?> +
+ + + $val) { + $GLOBALS['PMA_Config']->removeCookie('pmaPass-' . $key); + $GLOBALS['PMA_Config']->removeCookie('pmaServer-' . $key); + $GLOBALS['PMA_Config']->removeCookie('pmaUser-' . $key); + } + return false; + } + + if (! empty($_REQUEST['old_usr'])) { + // The user wants to be logged out + // -> delete his choices that were stored in session + + // according to the PHP manual we should do this before the destroy: + //$_SESSION = array(); + + session_destroy(); + // -> delete password cookie(s) + if ($GLOBALS['cfg']['LoginCookieDeleteAll']) { + foreach ($GLOBALS['cfg']['Servers'] as $key => $val) { + $GLOBALS['PMA_Config']->removeCookie('pmaPass-' . $key); + if (isset($_COOKIE['pmaPass-' . $key])) { + unset($_COOKIE['pmaPass-' . $key]); + } + } + } else { + $GLOBALS['PMA_Config']->removeCookie('pmaPass-' . $GLOBALS['server']); + if (isset($_COOKIE['pmaPass-' . $GLOBALS['server']])) { + unset($_COOKIE['pmaPass-' . $GLOBALS['server']]); + } + } + } + + if (! empty($_REQUEST['pma_username'])) { + // The user just logged in + $GLOBALS['PHP_AUTH_USER'] = $_REQUEST['pma_username']; + $GLOBALS['PHP_AUTH_PW'] = empty($_REQUEST['pma_password']) + ? '' + : $_REQUEST['pma_password']; + if ($GLOBALS['cfg']['AllowArbitraryServer'] + && isset($_REQUEST['pma_servername']) + ) { + $GLOBALS['pma_auth_server'] = $_REQUEST['pma_servername']; + } + return true; + } + + // At the end, try to set the $GLOBALS['PHP_AUTH_USER'] + // and $GLOBALS['PHP_AUTH_PW'] variables from cookies + + // servername + if ($GLOBALS['cfg']['AllowArbitraryServer'] + && ! empty($_COOKIE['pmaServer-' . $GLOBALS['server']]) + ) { + $GLOBALS['pma_auth_server'] = $_COOKIE['pmaServer-' . $GLOBALS['server']]; + } + + // username + if (empty($_COOKIE['pmaUser-' . $GLOBALS['server']])) { + return false; + } + + $GLOBALS['PHP_AUTH_USER'] = PMA_blowfish_decrypt( + $_COOKIE['pmaUser-' . $GLOBALS['server']], + PMA_get_blowfish_secret() + ); + + // user was never logged in since session start + if (empty($_SESSION['last_access_time'])) { + return false; + } + + // User inactive too long + if ($_SESSION['last_access_time'] < time() - $GLOBALS['cfg']['LoginCookieValidity']) { + PMA_cacheUnset('is_create_db_priv', true); + PMA_cacheUnset('is_process_priv', true); + PMA_cacheUnset('is_reload_priv', true); + PMA_cacheUnset('db_to_create', true); + PMA_cacheUnset('dbs_where_create_table_allowed', true); + $GLOBALS['no_activity'] = true; + PMA_auth_fails(); + exit; + } + + // password + if (empty($_COOKIE['pmaPass-' . $GLOBALS['server']])) { + return false; + } + + $GLOBALS['PHP_AUTH_PW'] = PMA_blowfish_decrypt( + $_COOKIE['pmaPass-' . $GLOBALS['server']], + PMA_get_blowfish_secret() + ); + + if ($GLOBALS['PHP_AUTH_PW'] == "\xff(blank)") { + $GLOBALS['PHP_AUTH_PW'] = ''; + } + + $GLOBALS['from_cookie'] = true; + + return true; } /** + * Set the user and password after last checkings if required * - * - * @return void + * @return boolean always true */ public function authSetUser() { + global $cfg; + + // Ensures valid authentication mode, 'only_db', bookmark database and + // table names and relation table name are used + if ($cfg['Server']['user'] != $GLOBALS['PHP_AUTH_USER']) { + foreach ($cfg['Servers'] as $idx => $current) { + if ($current['host'] == $cfg['Server']['host'] + && $current['port'] == $cfg['Server']['port'] + && $current['socket'] == $cfg['Server']['socket'] + && $current['ssl'] == $cfg['Server']['ssl'] + && $current['connect_type'] == $cfg['Server']['connect_type'] + && $current['user'] == $GLOBALS['PHP_AUTH_USER'] + ) { + $GLOBALS['server'] = $idx; + $cfg['Server'] = $current; + break; + } + } // end foreach + } // end if + + if ($GLOBALS['cfg']['AllowArbitraryServer'] + && ! empty($GLOBALS['pma_auth_server']) + ) { + /* Allow to specify 'host port' */ + $parts = explode(' ', $GLOBALS['pma_auth_server']); + if (count($parts) == 2) { + $tmp_host = $parts[0]; + $tmp_port = $parts[1]; + } else { + $tmp_host = $GLOBALS['pma_auth_server']; + $tmp_port = ''; + } + if ($cfg['Server']['host'] != $GLOBALS['pma_auth_server']) { + $cfg['Server']['host'] = $tmp_host; + if (! empty($tmp_port)) { + $cfg['Server']['port'] = $tmp_port; + } + } + unset($tmp_host, $tmp_port, $parts); + } + $cfg['Server']['user'] = $GLOBALS['PHP_AUTH_USER']; + $cfg['Server']['password'] = $GLOBALS['PHP_AUTH_PW']; + + // Avoid showing the password in phpinfo()'s output + unset($GLOBALS['PHP_AUTH_PW']); + unset($_SERVER['PHP_AUTH_PW']); + + $_SESSION['last_access_time'] = time(); + + // Name and password cookies need to be refreshed each time + // Duration = one month for username + $GLOBALS['PMA_Config']->setCookie( + 'pmaUser-' . $GLOBALS['server'], + PMA_blowfish_encrypt( + $cfg['Server']['user'], PMA_get_blowfish_secret() + ) + ); + + // Duration = as configured + $GLOBALS['PMA_Config']->setCookie( + 'pmaPass-' . $GLOBALS['server'], + PMA_blowfish_encrypt( + ! empty($cfg['Server']['password']) ? $cfg['Server']['password'] : "\xff(blank)", + PMA_get_blowfish_secret() + ), + null, + $GLOBALS['cfg']['LoginCookieStore'] + ); + + // Set server cookies if required (once per session) and, in this case, force + // reload to ensure the client accepts cookies + if (! $GLOBALS['from_cookie']) { + if ($GLOBALS['cfg']['AllowArbitraryServer']) { + if (! empty($GLOBALS['pma_auth_server'])) { + // Duration = one month for servername + $GLOBALS['PMA_Config']->setCookie( + 'pmaServer-' . $GLOBALS['server'], + $cfg['Server']['host'] + ); + } else { + // Delete servername cookie + $GLOBALS['PMA_Config']->removeCookie( + 'pmaServer-' . $GLOBALS['server'] + ); + } + } + + // URL where to go: + $redirect_url = $cfg['PmaAbsoluteUri'] . 'index.php'; + + // any parameters to pass? + $url_params = array(); + if (strlen($GLOBALS['db'])) { + $url_params['db'] = $GLOBALS['db']; + } + if (strlen($GLOBALS['table'])) { + $url_params['table'] = $GLOBALS['table']; + } + // any target to pass? + if (! empty($GLOBALS['target']) && $GLOBALS['target'] != 'index.php') { + $url_params['target'] = $GLOBALS['target']; + } + + /** + * Clear user cache. + */ + PMA_clearUserCache(); + + PMA_Response::getInstance()->disable(); + + PMA_sendHeaderLocation( + $redirect_url . PMA_generate_common_url($url_params, '&'), + true + ); + exit; + } // end if + + return true; + } /** + * User is not allowed to login to MySQL -> authentication failed * + * prepares error message and switches to PMA_auth() which display the error + * and the login form + * + * this function MUST exit/quit the application, + * currently doen by call to PMA_auth() * * @return void */ public function authFails() { + global $conn_error; + + // Deletes password cookie and displays the login form + $GLOBALS['PMA_Config']->removeCookie('pmaPass-' . $GLOBALS['server']); + + if (! empty($GLOBALS['login_without_password_is_forbidden'])) { + $conn_error = __('Login without a password is forbidden by configuration (see AllowNoPassword)'); + } elseif (! empty($GLOBALS['allowDeny_forbidden'])) { + $conn_error = __('Access denied'); + } elseif (! empty($GLOBALS['no_activity'])) { + $conn_error = sprintf(__('No activity within %s seconds; please log in again'), $GLOBALS['cfg']['LoginCookieValidity']); + // Remember where we got timeout to return on same place + if (PMA_getenv('SCRIPT_NAME')) { + $GLOBALS['target'] = basename(PMA_getenv('SCRIPT_NAME')); + // avoid "missing parameter: field" on re-entry + if ('tbl_alter.php' == $GLOBALS['target']) { + $GLOBALS['target'] = 'tbl_structure.php'; + } + } + } elseif (PMA_DBI_getError()) { + $conn_error = '#' . $GLOBALS['errno'] . ' ' + . __('Cannot log in to the MySQL server'); + } else { + $conn_error = __('Cannot log in to the MySQL server'); + } + + // needed for PHP-CGI (not need for FastCGI or mod-php) + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Pragma: no-cache'); + + PMA_auth(); } /** + * Returns blowfish secret or generates one if needed. * - * - * @return void + * @return string */ - private function blowfishEncrypt() + public function getBlowfishSecret () { + if (empty($GLOBALS['cfg']['blowfish_secret'])) { + if (empty($_SESSION['auto_blowfish_secret'])) { + // this returns 23 characters + $_SESSION['auto_blowfish_secret'] = uniqid('', true); + } + return $_SESSION['auto_blowfish_secret']; + } else { + // apply md5() to work around too long secrets (returns 32 characters) + return md5($GLOBALS['cfg']['blowfish_secret']); + } } /** + * Encryption using blowfish algorithm (mcrypt) * + * @param string $data original data + * @param string $secret the secret * - * @return void + * @return string the encrypted result */ - private function blowfishDecrypt() + public function blowfishEncrypt($data, $secret) { + if (! function_exists('mcrypt_encrypt')) { + include_once("HordeCipherBlowfishOperations.class.php"); + return HordeCipherBlowfishOperations::blowfishEncrypt($data, $secret); + } + + return base64_encode( + mcrypt_encrypt(MCRYPT_BLOWFISH, $secret, $data, MCRYPT_MODE_CBC, $this->_getIv()) + ); + } + + /** + * Decryption using blowfish algorithm (mcrypt) + * + * @param string $encdata encrypted data + * @param string $secret the secret + * + * @return string original data + */ + public function blowfishDecrypt($encdata, $secret) + { + if (! function_exists('mcrypt_encrypt')) { + include_once("HordeCipherBlowfishOperations.class.php"); + return HordeCipherBlowfishOperations::blowfishDecrypt( + $encdata, + $secret + ); + } + + $data = base64_decode($encdata); + $decrypted = mcrypt_decrypt( + MCRYPT_BLOWFISH, + $secret, + $data, + MCRYPT_MODE_CBC, + $this->_getIv() + ); + return trim($decrypted); } /** @@ -86,4 +660,30 @@ class AuthenticationCookie extends AuthenticationPlugin public function update (SplSubject $subject) { } + + + /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ + + + /** + * Get the initialization vector + * + * @return array + */ + private function _getIv() + { + return $this->_iv; + } + + /** + * Set the initialization vector + * + * @param array $iv the initialization vector + * + * @return void + */ + private function _setIv($iv) + { + $this->_iv = $iv; + } } \ No newline at end of file diff --git a/libraries/plugins/auth/HordeCipherBlowfishOperations.class.php b/libraries/plugins/auth/HordeCipherBlowfishOperations.class.php new file mode 100644 index 0000000000..8b6447008d --- /dev/null +++ b/libraries/plugins/auth/HordeCipherBlowfishOperations.class.php @@ -0,0 +1,67 @@ + 0) { + $data .= str_repeat("\0", 8 - $mod); + } + + foreach (str_split($data, 8) as $chunk) { + $encrypt .= $pma_cipher->encryptBlock($chunk, $secret); + } + return base64_encode($encrypt); + } + + /** + * Decryption using blowfish algorithm + * + * @param string $encdata encrypted data + * @param string $secret the secret + * + * @return string original data + */ + public static function blowfishDecrypt($encdata, $secret) + { + $pma_cipher = new Horde_Cipher_blowfish; + $decrypt = ''; + $data = base64_decode($encdata); + + foreach (str_split($data, 8) as $chunk) { + $decrypt .= $pma_cipher->decryptBlock($chunk, $secret); + } + return trim($decrypt); + } +} +?>