diff options
Diffstat (limited to 'hugo/libraries/plugins/auth/swekey/swekey.php')
| -rw-r--r-- | hugo/libraries/plugins/auth/swekey/swekey.php | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/hugo/libraries/plugins/auth/swekey/swekey.php b/hugo/libraries/plugins/auth/swekey/swekey.php new file mode 100644 index 0000000..d495d45 --- /dev/null +++ b/hugo/libraries/plugins/auth/swekey/swekey.php @@ -0,0 +1,522 @@ +<?php +/** + * Library that provides common functions that are used to help integrating Swekey Authentication in a PHP web site + * 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 + * + * @package Swekey + */ + + +/** + * Errors codes + */ +define("SWEKEY_ERR_INVALID_DEV_STATUS", 901); // The satus of the device is not SWEKEY_STATUS_OK +define("SWEKEY_ERR_INTERNAL", 902); // Should never occurd +define("SWEKEY_ERR_OUTDATED_RND_TOKEN", 910); // You random token is too old +define("SWEKEY_ERR_INVALID_OTP", 911); // The otp was not correct + +/** + * Those errors are considered as an attack and your site will be blacklisted during one minute + * if you receive one of those errors + */ +define("SWEKEY_ERR_BADLY_ENCODED_REQUEST", 920); +define("SWEKEY_ERR_INVALID_RND_TOKEN", 921); +define("SWEKEY_ERR_DEV_NOT_FOUND", 922); + +/** + * Default values for configuration. + */ +define('SWEKEY_DEFAULT_CHECK_SERVER', 'https://auth-check.musbe.net'); +define('SWEKEY_DEFAULT_RND_SERVER', 'https://auth-rnd-gen.musbe.net'); +define('SWEKEY_DEFAULT_STATUS_SERVER', 'https://auth-status.musbe.net'); + +/** + * The last error of an operation is alway put in this global var + */ + +global $gSwekeyLastError; +$gSwekeyLastError = 0; + +global $gSwekeyLastResult; +$gSwekeyLastResult = "<not set>"; + +/** + * Servers addresses + * Use the Swekey_SetXxxServer($server) functions to set them + */ + +global $gSwekeyCheckServer; +if (! isset($gSwekeyCheckServer)) { + $gSwekeyCheckServer = SWEKEY_DEFAULT_CHECK_SERVER; +} + +global $gSwekeyRndTokenServer; +if (! isset($gSwekeyRndTokenServer)) { + $gSwekeyRndTokenServer = SWEKEY_DEFAULT_RND_SERVER; +} + +global $gSwekeyStatusServer; +if (! isset($gSwekeyStatusServer)) { + $gSwekeyStatusServer = SWEKEY_DEFAULT_STATUS_SERVER; +} + +global $gSwekeyCA; + +global $gSwekeyTokenCacheEnabled; +if (! isset($gSwekeyTokenCacheEnabled)) { + $gSwekeyTokenCacheEnabled = true; +} + +/** + * Change the address of the Check server. + * If $server is empty the default value 'http://auth-check.musbe.net' will be used + * + * @param server The protocol and hostname to use + * + * @access public + */ +function Swekey_SetCheckServer($server) +{ + global $gSwekeyCheckServer; + if (empty($server)) { + $gSwekeyCheckServer = SWEKEY_DEFAULT_CHECK_SERVER; + } else { + $gSwekeyCheckServer = $server; + } +} + +/** + * Change the address of the Random Token Generator server. + * If $server is empty the default value 'http://auth-rnd-gen.musbe.net' will be used + * + * @param server The protocol and hostname to use + * + * @access public + */ +function Swekey_SetRndTokenServer($server) +{ + global $gSwekeyRndTokenServer; + if (empty($server)) { + $gSwekeyRndTokenServer = SWEKEY_DEFAULT_RND_SERVER; + } else { + $gSwekeyRndTokenServer = $server; + } +} + +/** + * Change the address of the Satus server. + * If $server is empty the default value 'http://auth-status.musbe.net' will be used + * + * @param server The protocol and hostname to use + * + * @access public + */ +function Swekey_SetStatusServer($server) +{ + global $gSwekeyStatusServer; + if (empty($server)) { + $gSwekeyStatusServer = SWEKEY_DEFAULT_STATUS_SERVER; + } else { + $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. + * + * @return The Last Error + * @access public + */ +function Swekey_GetLastError() +{ + global $gSwekeyLastError; + return $gSwekeyLastError; +} + +/** + * Return the last result. + * + * @return The Last Error + * @access public + */ +function Swekey_GetLastResult() +{ + global $gSwekeyLastResult; + return $gSwekeyLastResult; +} + +/** + * Send a synchronous request to the server. + * This function manages timeout then will not block if one of the server is down + * + * @param url The url to get + * @param response_code The response code + * + * @return The body of the response or "" in case of error + * @access private + */ +function Swekey_HttpGet($url, &$response_code) +{ + global $gSwekeyLastError; + $gSwekeyLastError = 0; + global $gSwekeyLastResult; + $gSwekeyLastResult = "<not set>"; + + // use curl if available + if (function_exists('curl_init')) { + $sess = curl_init($url); + if (substr($url, 0, 8) == "https://") { + global $gSwekeyCA; + + 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"); + } + } + + curl_setopt($sess, CURLOPT_SSL_VERIFYHOST, '2'); + curl_setopt($sess, CURLOPT_SSL_VERIFYPEER, '2'); + 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 + for ($num=1; $num <= 3; $num++ ) { + $r = new HttpRequest($url); + $options = array('timeout' => '3'); + + if (substr($url, 0, 6) == "https:") { + $sslOptions = array(); + $sslOptions['verifypeer'] = true; + $sslOptions['verifyhost'] = true; + + $capath = __FILE__; + $name = strrchr($capath, '/'); + // windows + if (empty($name)) { + $name = strrchr($capath, '\\'); + } + $capath = substr($capath, 0, strlen($capath) - strlen($name) + 1).'musbe-ca.crt'; + + if (! empty($gSwekeyCA)) { + $sslOptions['cainfo'] = $gSwekeyCA; + } + + $options['ssl'] = $sslOptions; + } + + $r->setOptions($options); + + // try + { + $reply = $r->send(); + $res = $reply->getBody(); + $info = $r->getResponseInfo(); + $response_code = $info['response_code']; + if ($response_code != 200) { + $gSwekeyLastError = $response_code; + error_log("SWEKEY_ERROR:Error ".$gSwekeyLastError." getting ".$url); + return ""; + } + + + $gSwekeyLastResult = $res; + return $res; + } + // catch (HttpException $e) + // { + // error_log("SWEKEY_WARNING:HttpException ".$e." getting ".$url); + // } + } + + $response_code = 408; // Request Timeout + $gSwekeyLastError = $response_code; + error_log("SWEKEY_ERROR:Error ".$gSwekeyLastError." getting ".$url); + return ""; + } + + global $http_response_header; + $res = @file_get_contents($url); + $response_code = substr($http_response_header[0], 9, 3); //HTTP/1.0 + if ($response_code == 200) { + $gSwekeyLastResult = $res; + return $res; + } + + $gSwekeyLastError = $response_code; + error_log("SWEKEY_ERROR:Error ".$response_code." getting ".$url); + return ""; +} + +/** + * Get a Random Token from a Token Server + * The RT is a 64 vhars hexadecimal value + * You should better use Swekey_GetFastRndToken() for performance + * @access public + */ +function Swekey_GetRndToken() +{ + global $gSwekeyRndTokenServer; + return Swekey_HttpGet($gSwekeyRndTokenServer.'/FULL-RND-TOKEN', $response_code); +} + +/** + * Get a Half Random Token from a Token Server + * The RT is a 64 vhars hexadecimal value + * Use this value if you want to make your own Swekey_GetFastRndToken() + * @access public + */ +function Swekey_GetHalfRndToken() +{ + global $gSwekeyRndTokenServer; + return Swekey_HttpGet($gSwekeyRndTokenServer.'/HALF-RND-TOKEN', $response_code); +} + +/** + * Get a Half Random Token + * The RT is a 64 vhars hexadecimal value + * This function get a new random token and reuse it. + * Token are refetched from the server only once every 30 seconds. + * You should always use this function to get half random token. + * @access public + */ +function Swekey_GetFastHalfRndToken() +{ + global $gSwekeyTokenCacheEnabled; + + $res = ""; + $cachefile = ""; + + // We check if we have a valid RT is the session + if (isset($_SESSION['rnd-token-date'])) { + if (time() - $_SESSION['rnd-token-date'] < 30) { + $res = $_SESSION['rnd-token']; + } + } + + // If not we try to get it from a temp file (PHP >= 5.2.1 only) + if (strlen($res) != 32 && $gSwekeyTokenCacheEnabled) { + if (function_exists('sys_get_temp_dir')) { + $tempdir = sys_get_temp_dir(); + $cachefile = $tempdir."/swekey-rnd-token-".get_current_user(); + $modif = filemtime($cachefile); + if ($modif != false) { + if (time() - $modif < 30) { + $res = @file_get_contents($cachefile); + if (strlen($res) != 32) { + $res = ""; + } else { + $_SESSION['rnd-token'] = $res; + $_SESSION['rnd-token-date'] = $modif; + } + } + } + } + } + + // If we don't have a valid RT here we have to get it from the server + if (strlen($res) != 32) { + $res = substr(Swekey_GetHalfRndToken(), 0, 32); + $_SESSION['rnd-token'] = $res; + $_SESSION['rnd-token-date'] = time(); + if (! empty($cachefile)) { + // we unlink the file so no possible tempfile race attack + unlink($cachefile); + $file = fopen($cachefile, "x"); + if ($file != false) { + @fwrite($file, $res); + @fclose($file); + } + } + } + + return $res."00000000000000000000000000000000"; +} + +/** + * Get a Random Token + * The RT is a 64 vhars hexadecimal value + * This function generates a unique random token for each call but call the + * server only once every 30 seconds. + * You should always use this function to get random token. + * @access public + */ +function Swekey_GetFastRndToken() +{ + $res = Swekey_GetFastHalfRndToken(); + if (strlen($res) == 64) { + return substr($res, 0, 32).strtoupper(md5("Musbe Authentication Key" . mt_rand() . date(DATE_ATOM))); + } + return ""; +} + + +/** + * Checks that an OTP generated by a Swekey is valid + * + * @param id The id of the swekey + * @param rt The random token used to generate the otp + * @param otp The otp generated by the swekey + * + * @return true or false + * @access public + */ +function Swekey_CheckOtp($id, $rt, $otp) +{ + global $gSwekeyCheckServer; + $res = Swekey_HttpGet($gSwekeyCheckServer.'/CHECK-OTP/'.$id.'/'.$rt.'/'.$otp, $response_code); + return $response_code == 200 && $res == "OK"; +} + +/** + * Values that are associated with a key. + * The following values can be returned by the Swekey_GetStatus() function + */ +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_STOLEN", 4); // The key was stolen +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 + +/** + * Values that are associated with a key. + * The Javascript Api can also return the following values + */ +define("SWEKEY_STATUS_REPLACED", 100); // This key has been replaced by a backup key +define("SWEKEY_STATUS_BACKUP_KEY", 101); // This key is a backup key that is not activated yet +define("SWEKEY_STATUS_NOTPLUGGED", 200); // This key is not plugged in the computer + + +/** + * Return the text corresponding to the integer status of a key + * + * @param status The status + * + * @return The text corresponding to the status + * @access public + */ +function Swekey_GetStatusStr($status) +{ + switch($status) + { + case SWEKEY_STATUS_OK : + return 'OK'; + case SWEKEY_STATUS_NOT_FOUND : + return 'Key does not exist in the db'; + case SWEKEY_STATUS_INACTIVE : + return 'Key not activated'; + case SWEKEY_STATUS_LOST : + return 'Key was lost'; + case SWEKEY_STATUS_STOLEN : + return 'Key was stolen'; + case SWEKEY_STATUS_FEE_DUE : + return 'The annual fee was not paid'; + case SWEKEY_STATUS_OBSOLETE : + return 'Key no longer supported'; + case SWEKEY_STATUS_REPLACED : + return 'This key has been replaced by a backup key'; + case SWEKEY_STATUS_BACKUP_KEY : + return 'This key is a backup key that is not activated yet'; + case SWEKEY_STATUS_NOTPLUGGED : + return 'This key is not plugged in the computer'; + case SWEKEY_STATUS_UNKOWN : + return 'Unknow Status, could not connect to the authentication server'; + } + return 'unknown status '.$status; +} + +/** + * If your web site requires a key to login you should check that the key + * is still valid (has not been lost or stolen) before requiring it. + * A key can be authenticated only if its status is SWEKEY_STATUS_OK + * + * @param id The id of the swekey + * + * @return The status of the swekey + * @access public + */ +function Swekey_GetStatus($id) +{ + global $gSwekeyStatusServer; + $res = Swekey_HttpGet($gSwekeyStatusServer.'/GET-STATUS/'.$id, $response_code); + if ($response_code == 200) { + return intval($res); + } + return SWEKEY_STATUS_UNKOWN; +} + +?> |
