From 930ee57a1c440815291101f1db394e69e1125016 Mon Sep 17 00:00:00 2001 From: Marc Delisle Date: Sat, 6 Sep 2008 12:41:34 +0000 Subject: [PATCH] latest Swekey fixes --- config.sample.inc.php | 2 +- contrib/swekey.sample.conf | 35 ++- libraries/auth/cookie.auth.lib.php | 37 +-- libraries/auth/swekey/authentication.inc.php | 142 +++++----- libraries/auth/swekey/musbe-ca.crt | 25 ++ libraries/auth/swekey/swekey.auth.lib.php | 260 ++++++++++++++----- libraries/auth/swekey/swekey.php | 127 ++++++++- 7 files changed, 446 insertions(+), 182 deletions(-) create mode 100644 libraries/auth/swekey/musbe-ca.crt diff --git a/config.sample.inc.php b/config.sample.inc.php index eedd3b308..a0225b26d 100644 --- a/config.sample.inc.php +++ b/config.sample.inc.php @@ -54,7 +54,7 @@ $cfg['Servers'][$i]['bs_temp_log_threshold'] = ''; // $cfg['Servers'][$i]['history'] = 'pma_history'; // $cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords'; /* Contrib / Swekey authentication */ -// $cfg['Servers'][$i]['auth_swekey_config'] = './swekey.conf'; +// $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf'; /* * End of servers configuration diff --git a/contrib/swekey.sample.conf b/contrib/swekey.sample.conf index 2c7088079..8ef706a57 100644 --- a/contrib/swekey.sample.conf +++ b/contrib/swekey.sample.conf @@ -1,12 +1,12 @@ # This is a typical file used to enable Swekey hardware authentication. # # To activate the Swekey authentication add the following line in your config.inc.php file. -# $cfg['Servers'][$i]['auth_swekey_config'] = './swekey.conf'; -# Then rename this file "swekey.conf" and copy it next to your config.inc.php file. +# $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf'; +# Then rename this file "swekey-pma.conf" and copy it in the /etc directory. # Add all the Swekey ids you want to grant access to in the file. # After each Swekey id put the corresponding user. # -# If you don't know the id of a Swekey just go on the http://auth-sample.musbe.com +# If you don't know the id of a Swekey just go on the http://www.swekey.com?sel=support # page while your Swekey is connected. # # If you need to purchase a Swekey please visit http://phpmyadmin.net/auth_key @@ -17,8 +17,29 @@ 000000000000000000000000000089E4:steve 0000000000000000000000000000231E:scott -# You can also uncomment the following lines if you want to use custom authentication servers +# +# Include following lines if you want to use authentication servers in ssl mode. +# Authentication is slower but more secure. +# -#SERVER_CHECK=http://auth-check.musbe.net -#SERVER_RNDTOKEN=http://auth-rnd-gen.musbe.net -#SERVER_STATUS=http://auth-status.musbe.net +SERVER_CHECK=https://auth-check-ssl.musbe.net +SERVER_RNDTOKEN=https://auth-rnd-gen-ssl.musbe.net +SERVER_STATUS=https://auth-status-ssl.musbe.net + + + +# +# In case of you want to use https servers you can set the path of the root certificate file +# + +#CA_FILE=/var/http-root/phpmyadmin/libraries/auth/swekey/musbe-ca.crt + +# +# If your sever receive lot of login requests, you can enable the random token caching to +# accelerate the authentication process. +# This is disabled by default because the cache file having full public access is vulnerable to +# a deny of service attack. +# You can enable it when your server is running in a secure environment. +# + +#ENABLE_TOKEN_CACHE=1 \ No newline at end of file diff --git a/libraries/auth/cookie.auth.lib.php b/libraries/auth/cookie.auth.lib.php index e2e4af24c..b2fcd6d9a 100644 --- a/libraries/auth/cookie.auth.lib.php +++ b/libraries/auth/cookie.auth.lib.php @@ -221,32 +221,7 @@ if (top != self) { // use fieldset, don't show doc link PMA_select_language(true, false); } - - // BEGIN Swekey Integration - $swekeyErr = Swekey_auth_error(); - if ($swekeyErr != null) { - PMA_Message::error($swekeyErr)->display(); - if ($GLOBALS['error_handler']->hasDisplayErrors()) { - echo '
'; - $GLOBALS['error_handler']->dispErrors(); - echo '
'; - } - echo '' . "\n"; - if (file_exists('./config.footer.inc.php')) { - require './config.footer.inc.php'; - } - echo ''; - exit; - } - - if (isset($_SESSION['PHP_AUTH_FORCE_USER'])) { - $default_user = $_SESSION['PHP_AUTH_FORCE_USER']; - $user_input_disabled = 'readonly="readonly"'; - } else { - $user_input_disabled = ''; - } - // END Swekey Integration - + ?>
@@ -268,7 +243,7 @@ if (top != self) {
- /> +
@@ -296,7 +271,7 @@ if (top != self) { ?>
- +
+ -?> -
- + diff --git a/libraries/auth/swekey/musbe-ca.crt b/libraries/auth/swekey/musbe-ca.crt new file mode 100644 index 000000000..2a31ad18f --- /dev/null +++ b/libraries/auth/swekey/musbe-ca.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIJAMjw7QcLWCd6MA0GCSqGSIb3DQEBBQUAMGsxCzAJBgNV +BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRQwEgYDVQQKEwtNdXNiZSwgSW5j +LjESMBAGA1UEAxMJbXVzYmUuY29tMR0wGwYJKoZIhvcNAQkBFg5pbmZvQG11c2Jl +LmNvbTAeFw0wODA5MDQxNDE2MTNaFw0zNzEyMjExNDE2MTNaMGsxCzAJBgNVBAYT +AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRQwEgYDVQQKEwtNdXNiZSwgSW5jLjES +MBAGA1UEAxMJbXVzYmUuY29tMR0wGwYJKoZIhvcNAQkBFg5pbmZvQG11c2JlLmNv +bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOBhOljxVzQfK4gted2I +d3BemcjW4abAUOzn3KYWXpPO5xIfVeXNDGkDbyH+X+7fo94sX25/ewuKNFDSOcvo +tXHq7uQenTHB35r+a+LY81KceUHgW90a3XsqPAkwAjyYcgo3zmM2DtLvw+5Yod8T +wAHk9m3qavnQ1uk99jBTwL7RZ9jIZHh9pFCL93uJc2obtd8O96Iycbn2q0w/AWbb ++eUVWIHzvLtfPvROeL3lJzr/Uz5LjKapxJ3qyqASflfHpnj9pU8l6g2TQ6Hg5KT5 +tLFkRe7uGhOfRtOQ/+NjaWrEuNCFnpyN4Q5Fv+5qA1Ip1IpH0200sWbAf/k2u0Qp +Sx0CAwEAAaOB0DCBzTAdBgNVHQ4EFgQUczJrQ7hCvtsnzcqiDIZ/GSn/CiwwgZ0G +A1UdIwSBlTCBkoAUczJrQ7hCvtsnzcqiDIZ/GSn/Ciyhb6RtMGsxCzAJBgNVBAYT +AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRQwEgYDVQQKEwtNdXNiZSwgSW5jLjES +MBAGA1UEAxMJbXVzYmUuY29tMR0wGwYJKoZIhvcNAQkBFg5pbmZvQG11c2JlLmNv +bYIJAMjw7QcLWCd6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGxk +8xzIljeBDQWWVRr0NEALVSv3i09V4jAKkyEOfmZ8lKMKJi0atwbtjrXTzLnNYj+Q +pyUbyY/8ItWvV7pnVxMiF9qcer7e9X4vw358GZuMVE/da1nWxz+CwzTm5oO30RzA +antM9bISFFr9lJq69bDWOnCUi1IG8DSL3TxtlABso7S4vqiZ+sB33l6k1K4a/Njb +QkU9UejKhKkVVZTsOrumfnOJ4MCmPfX8Y/AY2o670y5HnzpxerIYziCVzApPVrW7 +sKH0tuVGturMfQOKgstYe4/m9glBTeTLMkjD+6MJC2ONBD7GAiOO95gNl5M1fzJQ +FEe5CJ7DCYl0GdmLXXw= +-----END CERTIFICATE----- diff --git a/libraries/auth/swekey/swekey.auth.lib.php b/libraries/auth/swekey/swekey.auth.lib.php index 3e96c255c..4663fef9b 100644 --- a/libraries/auth/swekey/swekey.auth.lib.php +++ b/libraries/auth/swekey/swekey.auth.lib.php @@ -2,33 +2,51 @@ function Swekey_auth_check() { - // Load the swekey.conf file the first time - if (empty($_SESSION['PHP_AUTH_REQUIRED_SWEKEY'])) { - global $cfg; - $confFile = $cfg['Server']['auth_swekey_config']; - $_SESSION['PHP_AUTH_REQUIRES_SWEKEY'] = (! empty($confFile) && file_exists($confFile)); - if ($_SESSION['PHP_AUTH_REQUIRES_SWEKEY']) { - $_SESSION['PHP_AUTH_VALID_SWEKEYS'] = ""; - $_SESSION['PHP_AUTH_SERVER_CHECK'] = ""; - $_SESSION['PHP_AUTH_SERVER_RNDTOKEN'] = ""; - $_SESSION['PHP_AUTH_SERVER_STATUS'] = ""; - $valid_swekeys = split("\n",@file_get_contents($confFile)); - foreach ($valid_swekeys as $line) { - if (ereg("^[0-9A-F]{32}:.+$", $line) != false) - $_SESSION['PHP_AUTH_VALID_SWEKEYS'] .= trim($line) . ","; - else if (ereg("^SERVER_[A-Z]+=.*$", $line) != false) { - $items = explode("=", $line); - $_SESSION['PHP_AUTH_'.trim($items[0])] = trim($items[1]); - } - } - } - else - unset($_SESSION['PHP_AUTH_VALID_SWEKEYS']); + global $cfg; + $confFile = $cfg['Server']['auth_swekey_config']; + + if (! isset($_SESSION['SWEKEY'])) { + $_SESSION['SWEKEY'] = array(); } + + $_SESSION['SWEKEY']['ENABLED'] = (! empty($confFile) && file_exists($confFile)); + + // Load the swekey.conf file the first time + if ($_SESSION['SWEKEY']['ENABLED'] && empty($_SESSION['SWEKEY']['CONF_LOADED'])) { + $_SESSION['SWEKEY']['CONF_LOADED'] = true; + $_SESSION['SWEKEY']['VALID_SWEKEYS'] = array(); + $valid_swekeys = split("\n",@file_get_contents($confFile)); + foreach ($valid_swekeys as $line) { + if (ereg("^[0-9A-F]{32}:.+$", $line) != false) + { + $items = explode(":", $line); + if (count($items) == 2) + $_SESSION['SWEKEY']['VALID_SWEKEYS'][$items[0]] = trim($items[1]); + } + else if (ereg("^[A-Z_]+=.*$", $line) != false) { + $items = explode("=", $line); + $_SESSION['SWEKEY']['CONF_'.trim($items[0])] = trim($items[1]); + } + } + + // Set default values for settings + if (isset($_SESSION['SWEKEY']['CONF_SERVER_CHECK'])) + $_SESSION['SWEKEY']['CONF_SERVER_CHECK'] = ""; + if (! isset($_SESSION['SWEKEY']['CONF_SERVER_RNDTOKEN'])) + $_SESSION['SWEKEY']['CONF_SERVER_RNDTOKEN'] = ""; + if (! isset($_SESSION['SWEKEY']['CONF_SERVER_STATUS'])) + $_SESSION['SWEKEY']['CONF_SERVER_STATUS'] = ""; + if (! isset($_SESSION['SWEKEY']['CONF_CA_FILE'])) + $_SESSION['SWEKEY']['CONF_CA_FILE'] = ""; + if (! isset($_SESSION['SWEKEY']['CONF_ENABLE_TOKEN_CACHE'])) + $_SESSION['SWEKEY']['CONF_ENABLE_TOKEN_CACHE'] = false; + if (! isset($_SESSION['SWEKEY']['CONF_DEBUG'])) + $_SESSION['SWEKEY']['CONF_DEBUG'] = false; + } // check if a web key has been authenticated - if ($_SESSION['PHP_AUTH_REQUIRES_SWEKEY']) { - if (empty($_SESSION['PHP_AUTH_AUTHENTICATED_SWEKEY'])) + if ($_SESSION['SWEKEY']['ENABLED']) { + if (empty($_SESSION['SWEKEY']['AUTHENTICATED_SWEKEY'])) return false; } @@ -38,85 +56,197 @@ function Swekey_auth_check() function Swekey_auth_error() { - if (! $_SESSION['PHP_AUTH_REQUIRES_SWEKEY']) + if (! isset($_SESSION['SWEKEY'])) return null; - - if (! empty($_SESSION['PHP_AUTH_AUTHENTICATED_SWEKEY'])) + + if (! $_SESSION['SWEKEY']['ENABLED']) return null; - - if (empty($_SESSION['PHP_AUTH_VALID_SWEKEYS'])) + + require_once './libraries/auth/swekey/authentication.inc.php'; + + ?> + + \n"; +// if (file_exists($caFile)) +// echo "\n"; + } + if (file_exists($caFile)) + Swekey_SetCAFile($caFile); $result = null; parse_str($_SERVER['QUERY_STRING']); if (isset($swekey_id)) { - unset($_SESSION['PHP_AUTH_AUTHENTICATED_SWEKEY']); - if (! isset($_SESSION['PHP_AUTH_SWEKEY_RND_TOKEN'])) { + unset($_SESSION['SWEKEY']['AUTHENTICATED_SWEKEY']); + if (! isset($_SESSION['SWEKEY']['RND_TOKEN'])) { unset($swekey_id); } else { if (strlen($swekey_id) == 32) { - $res = Swekey_CheckOtp($swekey_id, $_SESSION['PHP_AUTH_SWEKEY_RND_TOKEN'], $swekey_otp); - unset($_SESSION['PHP_AUTH_SWEKEY_RND_TOKEN']); + $res = Swekey_CheckOtp($swekey_id, $_SESSION['SWEKEY']['RND_TOKEN'], $swekey_otp); + unset($_SESSION['SWEKEY']['RND_TOKEN']); if (! $res) { $result = $GLOBALS['strSwekeyAuthFailed'] . ' (' . Swekey_GetLastError() . ')'; } else { - $_SESSION['PHP_AUTH_AUTHENTICATED_SWEKEY'] = $swekey_id; - unset($_SESSION['PHP_AUTH_FORCE_USER']); - $valid_swekeys = split(",",$_SESSION['PHP_AUTH_VALID_SWEKEYS']); - foreach ($valid_swekeys as $line) { - if (substr($line,0,32) == $swekey_id) { - $_SESSION['PHP_AUTH_FORCE_USER'] = substr($line,33); - break; - } - } + $_SESSION['SWEKEY']['AUTHENTICATED_SWEKEY'] = $swekey_id; + $_SESSION['SWEKEY']['FORCE_USER'] = $_SESSION['SWEKEY']['VALID_SWEKEYS'][$swekey_id]; return null; } } else { $result = $GLOBALS['strSwekeyNoKey']; + if ($_SESSION['SWEKEY']['CONF_DEBUG']) + { + $result .= "
".$swekey_id; + } + unset($_SESSION['SWEKEY']['CONF_LOADED']); // reload the conf file } } } + else + unset($_SESSION['SWEKEY']); - $_SESSION['PHP_AUTH_SWEKEY_RND_TOKEN'] = Swekey_GetFastRndToken(); - if (strlen($_SESSION['PHP_AUTH_SWEKEY_RND_TOKEN']) != 64) { + $_SESSION['SWEKEY']['RND_TOKEN'] = Swekey_GetFastRndToken(); + if (strlen($_SESSION['SWEKEY']['RND_TOKEN']) != 64) { $result = $GLOBALS['strSwekeyAuthFailed'] . ' (' . Swekey_GetLastError() . ')'; + unset($_SESSION['SWEKEY']['CONF_LOADED']); // reload the conf file } - require_once './libraries/auth/swekey/authentication.inc.php'; - if (! isset($swekey_id)) { ?> - - display(); + if ($GLOBALS['error_handler']->hasDisplayErrors()) { + echo '
'; + $GLOBALS['error_handler']->dispErrors(); + echo '
'; + } + } + + if (isset($_SESSION['SWEKEY']) && $_SESSION['SWEKEY']['ENABLED']) { + echo ''; + } +} ?> diff --git a/libraries/auth/swekey/swekey.php b/libraries/auth/swekey/swekey.php index 9d2b5dbaf..94fb2b8eb 100644 --- a/libraries/auth/swekey/swekey.php +++ b/libraries/auth/swekey/swekey.php @@ -4,6 +4,9 @@ * Version 1.0 * * History: + * 1.2 Use curl (widely installed) to query the server + * Fixed a possible tempfile race attack + * Random token cache can now be disabled * 1.1 Added Swekey_HttpGet function that support faulty servers * Support for custom servers * 1.0 First release @@ -54,6 +57,12 @@ global $gSwekeyStatusServer; if (! isset($gSwekeyStatusServer)) $gSwekeyStatusServer = 'http://auth-status.musbe.net'; +global $gSwekeyCA; + +global $gSwekeyTokenCacheEnabled; +if (! isset($gSwekeyTokenCacheEnabled)) + $gSwekeyTokenCacheEnabled = false; + /** * Change the address of the Check server. * If $server is empty the default value 'http://auth-check.musbe.net' will be used @@ -102,6 +111,33 @@ function Swekey_SetStatusServer($server) $gSwekeyStatusServer = $server; } +/** + * Change the certificat file in case of the the severs use https instead of http + * + * @param cafile The path of the crt file to use + * @access public + */ +function Swekey_SetCAFile($cafile) +{ + global $gSwekeyCA; + $gSwekeyCA = $cafile; +} + +/** + * Enable or disable the random token caching + * Because everybody has full access to the cache file, it can be a DOS vulnerability + * So disable it if you are running in a non secure enviromnement + * + * @param $enable + * @access public + */ +function Swekey_EnableTokenCache($enable) +{ + global $gSwekeyTokenCacheEnabled; + $gSwekeyTokenCacheEnabled = ! empty($enable); +} + + /** * Return the last error. * @@ -142,7 +178,73 @@ function Swekey_HttpGet($url, &$response_code) global $gSwekeyLastResult; $gSwekeyLastResult = ""; - // you should install the pecl_http to be able to handle timeouts + // use curl if available + if (function_exists('curl_init')) + { + $sess = curl_init($url); + if (substr($url, 0, 8) == "https://") + { + global $gSwekeyCA; + $caFileOk = false; + if (! empty($gSwekeyCA)) + { + if (file_exists($gSwekeyCA)) + { + if (! curl_setopt($sess, CURLOPT_CAINFO, $gSwekeyCA)) + error_log("SWEKEY_ERROR:Could not set CA file : ".curl_error($sess)); + else + $caFileOk = true; + } + else + error_log("SWEKEY_ERROR:Could not find CA file $gSwekeyCA getting $url"); + } + + if ($caFileOk) + { + curl_setopt($sess, CURLOPT_SSL_VERIFYHOST, '1'); + curl_setopt($sess, CURLOPT_SSL_VERIFYPEER, '1'); + } + else + { + curl_setopt($sess, CURLOPT_SSL_VERIFYHOST, '0'); + curl_setopt($sess, CURLOPT_SSL_VERIFYPEER, '0'); + } + + curl_setopt($sess, CURLOPT_CONNECTTIMEOUT, '20'); + curl_setopt($sess, CURLOPT_TIMEOUT, '20'); + } + else + { + curl_setopt($sess, CURLOPT_CONNECTTIMEOUT, '3'); + curl_setopt($sess, CURLOPT_TIMEOUT, '5'); + } + + curl_setopt($sess, CURLOPT_RETURNTRANSFER, '1'); + $res=curl_exec($sess); + $response_code = curl_getinfo($sess, CURLINFO_HTTP_CODE); + $curlerr = curl_error($sess); + curl_close($sess); + + if ($response_code == 200) + { + $gSwekeyLastResult = $res; + return $res; + } + + if (! empty($response_code)) + { + $gSwekeyLastError = $response_code; + error_log("SWEKEY_ERROR:Error $gSwekeyLastError ($curlerr) getting $url"); + return ""; + } + + $response_code = 408; // Request Timeout + $gSwekeyLastError = $response_code; + error_log("SWEKEY_ERROR:Error $curlerr getting $url"); + return ""; + } + + // use pecl_http if available if (class_exists('HttpRequest')) { // retry if one of the server is down @@ -155,6 +257,7 @@ function Swekey_HttpGet($url, &$response_code) { $sslOptions = array(); $sslOptions['verifypeer'] = true; + $sslOptions['verifyhost'] = true; $capath = __FILE__; $name = strrchr($capath, '/'); @@ -162,8 +265,8 @@ function Swekey_HttpGet($url, &$response_code) $name = strrchr($capath, '\\'); $capath = substr($capath, 0, strlen($capath) - strlen($name) + 1).'musbe-ca.crt'; - if (file_exists($capath)) - $sslOptions['capath'] = $capath; + if (! empty($gSwekeyCA)) + $sslOptions['cainfo'] = $gSwekeyCA; $options['ssl'] = $sslOptions; } @@ -255,7 +358,7 @@ function Swekey_GetFastHalfRndToken() $res = $_SESSION['rnd-token']; // If not we try to get it from a temp file (PHP >= 5.2.1 only) - if (strlen($res) != 32) + if (strlen($res) != 32 && $gSwekeyTokenCacheEnabled) { if (function_exists('sys_get_temp_dir') ) { @@ -284,9 +387,15 @@ function Swekey_GetFastHalfRndToken() $_SESSION['rnd-token-date'] = time(); if (isset($tempdir)) { - $file = fopen ($tempdir."/swekey-rnd-token" , "w"); - @fwrite($file, $res); - @fclose($file); + // we unlink the file so no possible tempfile race attack (thanks Thijs) + unlink($tempdir."/swekey-rnd-token"); + $file = fopen ($tempdir."/swekey-rnd-token" , "x"); + if ($file != FALSE) + { + @fwrite($file, $res); + @fclose($file); + chmod($tempdir."/swekey-rnd-token", 0666); // it is a shared file everybody can read and write it + } } } @@ -335,8 +444,8 @@ define ("SWEKEY_STATUS_OK",0); define ("SWEKEY_STATUS_NOT_FOUND",1); // The key does not exist in the db define ("SWEKEY_STATUS_INACTIVE",2); // The key has never been activated define ("SWEKEY_STATUS_LOST",3); // The user has lost his key -define ("SWEKEY_STATUS_STOLLEN",4); // The key was stolen (typo kept for backward comp) -define ("SWEKEY_STATUS_STOLEN",4); // The key was stolen +define ("SWEKEY_STATUS_STOLLEN",4); // The key was stollen +define ("SWEKEY_STATUS_STOLEN",4); // The key was stollen define ("SWEKEY_STATUS_FEE_DUE",5); // The annual fee was not paid define ("SWEKEY_STATUS_OBSOLETE",6); // The hardware is no longer supported define ("SWEKEY_STATUS_UNKOWN",201); // We could not connect to the authentication server