mirror of
https://github.com/Yubico/yubikey-val.git
synced 2025-02-26 21:54:16 +01:00
Use LF as EOL consistently.
This commit is contained in:
parent
ce460ca737
commit
276616d871
2
NEWS
2
NEWS
@ -2,6 +2,8 @@
|
||||
|
||||
* Removed initial empty line from output for all commands.
|
||||
|
||||
* Use LF as EOL consistently.
|
||||
|
||||
* Updated release procedure.
|
||||
|
||||
* Version 2.22 (released 2013-03-12)
|
||||
|
544
ykval-common.php
544
ykval-common.php
@ -1,272 +1,272 @@
|
||||
<?php
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
define('S_OK', 'OK');
|
||||
define('S_BAD_OTP', 'BAD_OTP');
|
||||
define('S_REPLAYED_OTP', 'REPLAYED_OTP');
|
||||
define('S_DELAYED_OTP', 'DELAYED_OTP');
|
||||
define('S_BAD_SIGNATURE', 'BAD_SIGNATURE');
|
||||
define('S_MISSING_PARAMETER', 'MISSING_PARAMETER');
|
||||
define('S_NO_SUCH_CLIENT', 'NO_SUCH_CLIENT');
|
||||
define('S_OPERATION_NOT_ALLOWED', 'OPERATION_NOT_ALLOWED');
|
||||
define('S_BACKEND_ERROR', 'BACKEND_ERROR');
|
||||
define('S_NOT_ENOUGH_ANSWERS', 'NOT_ENOUGH_ANSWERS');
|
||||
define('S_REPLAYED_REQUEST', 'REPLAYED_REQUEST');
|
||||
|
||||
|
||||
define('TS_SEC', 1/8);
|
||||
define('TS_REL_TOLERANCE', 0.3);
|
||||
define('TS_ABS_TOLERANCE', 20);
|
||||
|
||||
define('TOKEN_LEN', 32);
|
||||
define('OTP_MAX_LEN', 48); // TOKEN_LEN plus public identity of 0..16
|
||||
|
||||
function logdie ($logger, $str)
|
||||
{
|
||||
$logger->log(LOG_INFO, $str);
|
||||
die($str . "\n");
|
||||
}
|
||||
|
||||
function unescape($s) {
|
||||
return str_replace('\\', "", $s);
|
||||
}
|
||||
|
||||
function getHttpVal($key, $defaultVal) {
|
||||
$val = $defaultVal;
|
||||
if (array_key_exists($key, $_GET)) {
|
||||
$val = $_GET[$key];
|
||||
} else if (array_key_exists($key, $_POST)) {
|
||||
$val = $_POST[$key];
|
||||
}
|
||||
$v = unescape(trim($val));
|
||||
return $v;
|
||||
}
|
||||
|
||||
function log_format() {
|
||||
$str = "";
|
||||
foreach (func_get_args() as $msg)
|
||||
{
|
||||
if (is_array($msg)) {
|
||||
foreach($msg as $key => $value){
|
||||
$str .= "$key=$value ";
|
||||
}
|
||||
} else {
|
||||
$str .= $msg . " ";
|
||||
}
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
// Return eg. 2008-11-21T06:11:55Z0711
|
||||
//
|
||||
function getUTCTimeStamp() {
|
||||
date_default_timezone_set('UTC');
|
||||
$tiny = substr(microtime(false), 2, 3);
|
||||
return date('Y-m-d\TH:i:s\Z0', time()) . $tiny;
|
||||
}
|
||||
|
||||
# NOTE: When we evolve to using general DB-interface, this functinality
|
||||
# should be moved there.
|
||||
function DbTimeToUnix($db_time)
|
||||
{
|
||||
$unix=strptime($db_time, '%F %H:%M:%S');
|
||||
return mktime($unix[tm_hour], $unix[tm_min], $unix[tm_sec], $unix[tm_mon]+1, $unix[tm_mday], $unix[tm_year]+1900);
|
||||
}
|
||||
|
||||
function UnixToDbTime($unix)
|
||||
{
|
||||
return date('Y-m-d H:i:s', $unix);
|
||||
}
|
||||
|
||||
// Sign a http query string in the array of key-value pairs
|
||||
// return b64 encoded hmac hash
|
||||
function sign($a, $apiKey, $logger) {
|
||||
ksort($a);
|
||||
$qs = urldecode(http_build_query($a));
|
||||
|
||||
// the TRUE at the end states we want the raw value, not hexadecimal form
|
||||
$hmac = hash_hmac('sha1', utf8_encode($qs), $apiKey, true);
|
||||
$hmac = base64_encode($hmac);
|
||||
|
||||
$logger->log(LOG_DEBUG, 'SIGN: ' . $qs . ' H=' . $hmac);
|
||||
|
||||
return $hmac;
|
||||
|
||||
} // sign an array of query string
|
||||
|
||||
function hex2b64 ($hex_str) {
|
||||
$bin = pack("H*", $hex_str);
|
||||
return base64_encode($bin);
|
||||
}
|
||||
|
||||
function modhex2b64 ($modhex_str) {
|
||||
$hex_str = strtr ($modhex_str, "cbdefghijklnrtuv", "0123456789abcdef");
|
||||
return hex2b64($hex_str);
|
||||
}
|
||||
|
||||
// This function takes a list of URLs. It will return the content of
|
||||
// the first successfully retrieved URL, whose content matches ^OK.
|
||||
// The request are sent asynchronously. Some of the URLs can fail
|
||||
// with unknown host, connection errors, or network timeout, but as
|
||||
// long as one of the URLs given work, data will be returned. If all
|
||||
// URLs fail, data from some URL that did not match parameter $match
|
||||
// (defaults to ^OK) is returned, or if all URLs failed, false.
|
||||
function retrieveURLasync ($ident, $urls, $logger, $ans_req=1, $match="^OK", $returl=False, $timeout=10) {
|
||||
$mh = curl_multi_init();
|
||||
|
||||
$ch = array();
|
||||
foreach ($urls as $id => $url) {
|
||||
$handle = curl_init();
|
||||
$logger->log(LOG_DEBUG, $ident . " adding URL : " . $url);
|
||||
curl_setopt($handle, CURLOPT_URL, $url);
|
||||
curl_setopt($handle, CURLOPT_USERAGENT, "YK-VAL");
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($handle, CURLOPT_FAILONERROR, true);
|
||||
curl_setopt($handle, CURLOPT_TIMEOUT, $timeout);
|
||||
|
||||
curl_multi_add_handle($mh, $handle);
|
||||
|
||||
$ch[$handle] = $handle;
|
||||
}
|
||||
|
||||
$str = false;
|
||||
$ans_count = 0;
|
||||
$ans_arr = array();
|
||||
|
||||
do {
|
||||
while (($mrc = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM)
|
||||
;
|
||||
|
||||
while ($info = curl_multi_info_read($mh)) {
|
||||
$logger->log(LOG_DEBUG, $ident . " curl multi info : ", $info);
|
||||
if ($info['result'] == CURLE_OK) {
|
||||
$str = curl_multi_getcontent($info['handle']);
|
||||
$logger->log(LOG_DEBUG, $ident . " curl multi content : " . $str);
|
||||
if (preg_match("/".$match."/", $str)) {
|
||||
$logger->log(LOG_DEBUG, $ident . " response matches " . $match);
|
||||
$error = curl_error ($info['handle']);
|
||||
$errno = curl_errno ($info['handle']);
|
||||
$cinfo = curl_getinfo ($info['handle']);
|
||||
$logger->log(LOG_DEBUG, $ident . " errno/error: " . $errno . "/" . $error, $cinfo);
|
||||
$ans_count++;
|
||||
if ($returl) $ans_arr[]="url=" . $cinfo['url'] . "\n" . $str;
|
||||
else $ans_arr[]=$str;
|
||||
}
|
||||
|
||||
if ($ans_count >= $ans_req) {
|
||||
foreach ($ch as $h) {
|
||||
curl_multi_remove_handle ($mh, $h);
|
||||
curl_close ($h);
|
||||
}
|
||||
curl_multi_close ($mh);
|
||||
|
||||
return $ans_arr;
|
||||
}
|
||||
|
||||
curl_multi_remove_handle ($mh, $info['handle']);
|
||||
curl_close ($info['handle']);
|
||||
unset ($ch[$info['handle']]);
|
||||
}
|
||||
|
||||
curl_multi_select ($mh);
|
||||
}
|
||||
} while($active);
|
||||
|
||||
foreach ($ch as $h) {
|
||||
curl_multi_remove_handle ($mh, $h);
|
||||
curl_close ($h);
|
||||
}
|
||||
curl_multi_close ($mh);
|
||||
|
||||
if ($ans_count>0) return $ans_arr;
|
||||
return $str;
|
||||
}
|
||||
|
||||
function retrieveURLsimple ($url, $match="^OK") {
|
||||
foreach (file($url) as $line) {
|
||||
if (preg_match("/".$match."/", $line)) {
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// $otp: A yubikey OTP
|
||||
function KSMdecryptOTP($urls, $logger) {
|
||||
$ret = array();
|
||||
if (!is_array($urls)) {
|
||||
$response = retrieveURLsimple ($urls);
|
||||
} elseif (count($urls) == 1) {
|
||||
$response = retrieveURLsimple ($urls[0]);
|
||||
} else {
|
||||
$response = retrieveURLasync ("YK-KSM", $urls, $logger, $ans_req=1, $match="^OK", $returl=False, $timeout=10);
|
||||
if (is_array($response)) {
|
||||
$response = $response[0];
|
||||
}
|
||||
}
|
||||
if ($response) {
|
||||
$logger->log(LOG_DEBUG, log_format("YK-KSM response: ", $response));
|
||||
}
|
||||
if (sscanf ($response,
|
||||
"OK counter=%04x low=%04x high=%02x use=%02x",
|
||||
$ret["session_counter"], $ret["low"], $ret["high"],
|
||||
$ret["session_use"]) != 4) {
|
||||
return false;
|
||||
}
|
||||
return $ret;
|
||||
} // End decryptOTP
|
||||
|
||||
function sendResp($status, $logger, $apiKey = '', $extra = null) {
|
||||
if ($status == null) {
|
||||
$status = S_BACKEND_ERROR;
|
||||
}
|
||||
|
||||
$a['status'] = $status;
|
||||
$a['t'] = getUTCTimeStamp();
|
||||
if ($extra){
|
||||
foreach ($extra as $param => $value) $a[$param] = $value;
|
||||
}
|
||||
$h = sign($a, $apiKey, $logger);
|
||||
|
||||
$str = "h=" . $h . "\r\n";
|
||||
$str .= "t=" . ($a['t']) . "\r\n";
|
||||
if ($extra){
|
||||
foreach ($extra as $param => $value) {
|
||||
$str .= $param . "=" . $value . "\r\n";
|
||||
}
|
||||
}
|
||||
$str .= "status=" . ($a['status']) . "\r\n";
|
||||
$str .= "\r\n";
|
||||
|
||||
$logger->log(LOG_INFO, "Response: " . $str .
|
||||
" (at " . date("c") . " " . microtime() . ")");
|
||||
|
||||
echo $str;
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
define('S_OK', 'OK');
|
||||
define('S_BAD_OTP', 'BAD_OTP');
|
||||
define('S_REPLAYED_OTP', 'REPLAYED_OTP');
|
||||
define('S_DELAYED_OTP', 'DELAYED_OTP');
|
||||
define('S_BAD_SIGNATURE', 'BAD_SIGNATURE');
|
||||
define('S_MISSING_PARAMETER', 'MISSING_PARAMETER');
|
||||
define('S_NO_SUCH_CLIENT', 'NO_SUCH_CLIENT');
|
||||
define('S_OPERATION_NOT_ALLOWED', 'OPERATION_NOT_ALLOWED');
|
||||
define('S_BACKEND_ERROR', 'BACKEND_ERROR');
|
||||
define('S_NOT_ENOUGH_ANSWERS', 'NOT_ENOUGH_ANSWERS');
|
||||
define('S_REPLAYED_REQUEST', 'REPLAYED_REQUEST');
|
||||
|
||||
|
||||
define('TS_SEC', 1/8);
|
||||
define('TS_REL_TOLERANCE', 0.3);
|
||||
define('TS_ABS_TOLERANCE', 20);
|
||||
|
||||
define('TOKEN_LEN', 32);
|
||||
define('OTP_MAX_LEN', 48); // TOKEN_LEN plus public identity of 0..16
|
||||
|
||||
function logdie ($logger, $str)
|
||||
{
|
||||
$logger->log(LOG_INFO, $str);
|
||||
die($str . "\n");
|
||||
}
|
||||
|
||||
function unescape($s) {
|
||||
return str_replace('\\', "", $s);
|
||||
}
|
||||
|
||||
function getHttpVal($key, $defaultVal) {
|
||||
$val = $defaultVal;
|
||||
if (array_key_exists($key, $_GET)) {
|
||||
$val = $_GET[$key];
|
||||
} else if (array_key_exists($key, $_POST)) {
|
||||
$val = $_POST[$key];
|
||||
}
|
||||
$v = unescape(trim($val));
|
||||
return $v;
|
||||
}
|
||||
|
||||
function log_format() {
|
||||
$str = "";
|
||||
foreach (func_get_args() as $msg)
|
||||
{
|
||||
if (is_array($msg)) {
|
||||
foreach($msg as $key => $value){
|
||||
$str .= "$key=$value ";
|
||||
}
|
||||
} else {
|
||||
$str .= $msg . " ";
|
||||
}
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
// Return eg. 2008-11-21T06:11:55Z0711
|
||||
//
|
||||
function getUTCTimeStamp() {
|
||||
date_default_timezone_set('UTC');
|
||||
$tiny = substr(microtime(false), 2, 3);
|
||||
return date('Y-m-d\TH:i:s\Z0', time()) . $tiny;
|
||||
}
|
||||
|
||||
# NOTE: When we evolve to using general DB-interface, this functinality
|
||||
# should be moved there.
|
||||
function DbTimeToUnix($db_time)
|
||||
{
|
||||
$unix=strptime($db_time, '%F %H:%M:%S');
|
||||
return mktime($unix[tm_hour], $unix[tm_min], $unix[tm_sec], $unix[tm_mon]+1, $unix[tm_mday], $unix[tm_year]+1900);
|
||||
}
|
||||
|
||||
function UnixToDbTime($unix)
|
||||
{
|
||||
return date('Y-m-d H:i:s', $unix);
|
||||
}
|
||||
|
||||
// Sign a http query string in the array of key-value pairs
|
||||
// return b64 encoded hmac hash
|
||||
function sign($a, $apiKey, $logger) {
|
||||
ksort($a);
|
||||
$qs = urldecode(http_build_query($a));
|
||||
|
||||
// the TRUE at the end states we want the raw value, not hexadecimal form
|
||||
$hmac = hash_hmac('sha1', utf8_encode($qs), $apiKey, true);
|
||||
$hmac = base64_encode($hmac);
|
||||
|
||||
$logger->log(LOG_DEBUG, 'SIGN: ' . $qs . ' H=' . $hmac);
|
||||
|
||||
return $hmac;
|
||||
|
||||
} // sign an array of query string
|
||||
|
||||
function hex2b64 ($hex_str) {
|
||||
$bin = pack("H*", $hex_str);
|
||||
return base64_encode($bin);
|
||||
}
|
||||
|
||||
function modhex2b64 ($modhex_str) {
|
||||
$hex_str = strtr ($modhex_str, "cbdefghijklnrtuv", "0123456789abcdef");
|
||||
return hex2b64($hex_str);
|
||||
}
|
||||
|
||||
// This function takes a list of URLs. It will return the content of
|
||||
// the first successfully retrieved URL, whose content matches ^OK.
|
||||
// The request are sent asynchronously. Some of the URLs can fail
|
||||
// with unknown host, connection errors, or network timeout, but as
|
||||
// long as one of the URLs given work, data will be returned. If all
|
||||
// URLs fail, data from some URL that did not match parameter $match
|
||||
// (defaults to ^OK) is returned, or if all URLs failed, false.
|
||||
function retrieveURLasync ($ident, $urls, $logger, $ans_req=1, $match="^OK", $returl=False, $timeout=10) {
|
||||
$mh = curl_multi_init();
|
||||
|
||||
$ch = array();
|
||||
foreach ($urls as $id => $url) {
|
||||
$handle = curl_init();
|
||||
$logger->log(LOG_DEBUG, $ident . " adding URL : " . $url);
|
||||
curl_setopt($handle, CURLOPT_URL, $url);
|
||||
curl_setopt($handle, CURLOPT_USERAGENT, "YK-VAL");
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($handle, CURLOPT_FAILONERROR, true);
|
||||
curl_setopt($handle, CURLOPT_TIMEOUT, $timeout);
|
||||
|
||||
curl_multi_add_handle($mh, $handle);
|
||||
|
||||
$ch[$handle] = $handle;
|
||||
}
|
||||
|
||||
$str = false;
|
||||
$ans_count = 0;
|
||||
$ans_arr = array();
|
||||
|
||||
do {
|
||||
while (($mrc = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM)
|
||||
;
|
||||
|
||||
while ($info = curl_multi_info_read($mh)) {
|
||||
$logger->log(LOG_DEBUG, $ident . " curl multi info : ", $info);
|
||||
if ($info['result'] == CURLE_OK) {
|
||||
$str = curl_multi_getcontent($info['handle']);
|
||||
$logger->log(LOG_DEBUG, $ident . " curl multi content : " . $str);
|
||||
if (preg_match("/".$match."/", $str)) {
|
||||
$logger->log(LOG_DEBUG, $ident . " response matches " . $match);
|
||||
$error = curl_error ($info['handle']);
|
||||
$errno = curl_errno ($info['handle']);
|
||||
$cinfo = curl_getinfo ($info['handle']);
|
||||
$logger->log(LOG_DEBUG, $ident . " errno/error: " . $errno . "/" . $error, $cinfo);
|
||||
$ans_count++;
|
||||
if ($returl) $ans_arr[]="url=" . $cinfo['url'] . "\n" . $str;
|
||||
else $ans_arr[]=$str;
|
||||
}
|
||||
|
||||
if ($ans_count >= $ans_req) {
|
||||
foreach ($ch as $h) {
|
||||
curl_multi_remove_handle ($mh, $h);
|
||||
curl_close ($h);
|
||||
}
|
||||
curl_multi_close ($mh);
|
||||
|
||||
return $ans_arr;
|
||||
}
|
||||
|
||||
curl_multi_remove_handle ($mh, $info['handle']);
|
||||
curl_close ($info['handle']);
|
||||
unset ($ch[$info['handle']]);
|
||||
}
|
||||
|
||||
curl_multi_select ($mh);
|
||||
}
|
||||
} while($active);
|
||||
|
||||
foreach ($ch as $h) {
|
||||
curl_multi_remove_handle ($mh, $h);
|
||||
curl_close ($h);
|
||||
}
|
||||
curl_multi_close ($mh);
|
||||
|
||||
if ($ans_count>0) return $ans_arr;
|
||||
return $str;
|
||||
}
|
||||
|
||||
function retrieveURLsimple ($url, $match="^OK") {
|
||||
foreach (file($url) as $line) {
|
||||
if (preg_match("/".$match."/", $line)) {
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// $otp: A yubikey OTP
|
||||
function KSMdecryptOTP($urls, $logger) {
|
||||
$ret = array();
|
||||
if (!is_array($urls)) {
|
||||
$response = retrieveURLsimple ($urls);
|
||||
} elseif (count($urls) == 1) {
|
||||
$response = retrieveURLsimple ($urls[0]);
|
||||
} else {
|
||||
$response = retrieveURLasync ("YK-KSM", $urls, $logger, $ans_req=1, $match="^OK", $returl=False, $timeout=10);
|
||||
if (is_array($response)) {
|
||||
$response = $response[0];
|
||||
}
|
||||
}
|
||||
if ($response) {
|
||||
$logger->log(LOG_DEBUG, log_format("YK-KSM response: ", $response));
|
||||
}
|
||||
if (sscanf ($response,
|
||||
"OK counter=%04x low=%04x high=%02x use=%02x",
|
||||
$ret["session_counter"], $ret["low"], $ret["high"],
|
||||
$ret["session_use"]) != 4) {
|
||||
return false;
|
||||
}
|
||||
return $ret;
|
||||
} // End decryptOTP
|
||||
|
||||
function sendResp($status, $logger, $apiKey = '', $extra = null) {
|
||||
if ($status == null) {
|
||||
$status = S_BACKEND_ERROR;
|
||||
}
|
||||
|
||||
$a['status'] = $status;
|
||||
$a['t'] = getUTCTimeStamp();
|
||||
if ($extra){
|
||||
foreach ($extra as $param => $value) $a[$param] = $value;
|
||||
}
|
||||
$h = sign($a, $apiKey, $logger);
|
||||
|
||||
$str = "h=" . $h . "\r\n";
|
||||
$str .= "t=" . ($a['t']) . "\r\n";
|
||||
if ($extra){
|
||||
foreach ($extra as $param => $value) {
|
||||
$str .= $param . "=" . $value . "\r\n";
|
||||
}
|
||||
}
|
||||
$str .= "status=" . ($a['status']) . "\r\n";
|
||||
$str .= "\r\n";
|
||||
|
||||
$logger->log(LOG_INFO, "Response: " . $str .
|
||||
" (at " . date("c") . " " . microtime() . ")");
|
||||
|
||||
echo $str;
|
||||
}
|
||||
?>
|
||||
|
202
ykval-config.php
202
ykval-config.php
@ -1,101 +1,101 @@
|
||||
<?php # -*- php -*-
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//ykval will use the configuration stored in /etc/yubico/val/config-db.php, if that file exists. If it does not exist, the below values will be used.
|
||||
|
||||
if(!include '/etc/yubico/val/config-db.php') {
|
||||
$dbuser='ykval_verifier';
|
||||
$dbpass='yourpassword';
|
||||
$basepath='';
|
||||
$dbname='ykval';
|
||||
$dbserver='';
|
||||
$dbport='';
|
||||
$dbtype='mysql';
|
||||
}
|
||||
|
||||
|
||||
# For the validation interface.
|
||||
$baseParams = array ();
|
||||
$baseParams['__YKVAL_DB_DSN__'] = "$dbtype:dbname=$dbname;host=127.0.0.1"; # "oci:oracledb" for Oracle DB (with OCI library)
|
||||
$baseParams['__YKVAL_DB_USER__'] = $dbuser;
|
||||
$baseParams['__YKVAL_DB_PW__'] = $dbpass;
|
||||
$baseParams['__YKVAL_DB_OPTIONS__'] = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
# For the validation server sync
|
||||
$baseParams['__YKVAL_SYNC_POOL__'] = array(/*"http://api2.example.com/wsapi/2.0/sync",*/
|
||||
/*"http://api3.example.com/wsapi/2.0/sync",*/
|
||||
/* "http://api4.example.com/wsapi/2.0/sync"*/);
|
||||
# An array of IP addresses allowed to issue sync requests
|
||||
# NOTE: You must use IP addresses here.
|
||||
$baseParams['__YKVAL_ALLOWED_SYNC_POOL__'] = array(/*"1.2.3.4",*/
|
||||
/*"2.3.4.5",*/
|
||||
/*"3.4.5.6"*/);
|
||||
|
||||
# An array of IP addresses allowed to issue YubiKey activation/deactivation
|
||||
# requests through ykval-revoke.php. NOTE: You must use IP addresses here.
|
||||
$baseParams['__YKREV_IPS__'] = array(/*"127.0.0.1"*/);
|
||||
# An array of IP addresses allowed to issue database resync requests through
|
||||
# ykval-resync.php. NOTE: You must use IP addresses here.
|
||||
#$baseParams['__YKRESYNC_IPS__'] = array("127.0.0.1");
|
||||
#Use the same as for issuing sync requests:
|
||||
$baseParams['__YKRESYNC_IPS__'] = $baseParams['__YKVAL_ALLOWED_SYNC_POOL__'];
|
||||
|
||||
# Specify how often the sync daemon awakens
|
||||
$baseParams['__YKVAL_SYNC_INTERVAL__'] = 10;
|
||||
# Specify how long the sync daemon will wait for response
|
||||
$baseParams['__YKVAL_SYNC_RESYNC_TIMEOUT__'] = 30;
|
||||
# Specify how old entries in the database should be considered aborted attempts
|
||||
$baseParams['__YKVAL_SYNC_OLD_LIMIT__'] = 10;
|
||||
|
||||
# These are settings for the validation server.
|
||||
$baseParams['__YKVAL_SYNC_FAST_LEVEL__'] = 1;
|
||||
$baseParams['__YKVAL_SYNC_SECURE_LEVEL__'] = 40;
|
||||
$baseParams['__YKVAL_SYNC_DEFAULT_LEVEL__'] = 60;
|
||||
$baseParams['__YKVAL_SYNC_DEFAULT_TIMEOUT__'] = 1;
|
||||
|
||||
// otp2ksmurls: Return array of YK-KSM URLs for decrypting OTP for
|
||||
// CLIENT. The URLs must be fully qualified, i.e., contain the OTP
|
||||
// itself.
|
||||
function otp2ksmurls ($otp, $client) {
|
||||
//if ($client == 42) {
|
||||
// return array("http://another-ykkms.example.com/wsapi/decrypt?otp=$otp");
|
||||
//}
|
||||
|
||||
//if (preg_match ("/^dteffujehknh/", $otp)) {
|
||||
// return array("http://different-ykkms.example.com/wsapi/decrypt?otp=$otp");
|
||||
//}
|
||||
|
||||
return array(
|
||||
//"http://ykkms1.example.com/wsapi/decrypt?otp=$otp",
|
||||
//"http://ykkms2.example.com/wsapi/decrypt?otp=$otp",
|
||||
"http://127.0.0.1/wsapi/decrypt?otp=$otp"
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
<?php # -*- php -*-
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//ykval will use the configuration stored in /etc/yubico/val/config-db.php, if that file exists. If it does not exist, the below values will be used.
|
||||
|
||||
if(!include '/etc/yubico/val/config-db.php') {
|
||||
$dbuser='ykval_verifier';
|
||||
$dbpass='yourpassword';
|
||||
$basepath='';
|
||||
$dbname='ykval';
|
||||
$dbserver='';
|
||||
$dbport='';
|
||||
$dbtype='mysql';
|
||||
}
|
||||
|
||||
|
||||
# For the validation interface.
|
||||
$baseParams = array ();
|
||||
$baseParams['__YKVAL_DB_DSN__'] = "$dbtype:dbname=$dbname;host=127.0.0.1"; # "oci:oracledb" for Oracle DB (with OCI library)
|
||||
$baseParams['__YKVAL_DB_USER__'] = $dbuser;
|
||||
$baseParams['__YKVAL_DB_PW__'] = $dbpass;
|
||||
$baseParams['__YKVAL_DB_OPTIONS__'] = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
# For the validation server sync
|
||||
$baseParams['__YKVAL_SYNC_POOL__'] = array(/*"http://api2.example.com/wsapi/2.0/sync",*/
|
||||
/*"http://api3.example.com/wsapi/2.0/sync",*/
|
||||
/* "http://api4.example.com/wsapi/2.0/sync"*/);
|
||||
# An array of IP addresses allowed to issue sync requests
|
||||
# NOTE: You must use IP addresses here.
|
||||
$baseParams['__YKVAL_ALLOWED_SYNC_POOL__'] = array(/*"1.2.3.4",*/
|
||||
/*"2.3.4.5",*/
|
||||
/*"3.4.5.6"*/);
|
||||
|
||||
# An array of IP addresses allowed to issue YubiKey activation/deactivation
|
||||
# requests through ykval-revoke.php. NOTE: You must use IP addresses here.
|
||||
$baseParams['__YKREV_IPS__'] = array(/*"127.0.0.1"*/);
|
||||
# An array of IP addresses allowed to issue database resync requests through
|
||||
# ykval-resync.php. NOTE: You must use IP addresses here.
|
||||
#$baseParams['__YKRESYNC_IPS__'] = array("127.0.0.1");
|
||||
#Use the same as for issuing sync requests:
|
||||
$baseParams['__YKRESYNC_IPS__'] = $baseParams['__YKVAL_ALLOWED_SYNC_POOL__'];
|
||||
|
||||
# Specify how often the sync daemon awakens
|
||||
$baseParams['__YKVAL_SYNC_INTERVAL__'] = 10;
|
||||
# Specify how long the sync daemon will wait for response
|
||||
$baseParams['__YKVAL_SYNC_RESYNC_TIMEOUT__'] = 30;
|
||||
# Specify how old entries in the database should be considered aborted attempts
|
||||
$baseParams['__YKVAL_SYNC_OLD_LIMIT__'] = 10;
|
||||
|
||||
# These are settings for the validation server.
|
||||
$baseParams['__YKVAL_SYNC_FAST_LEVEL__'] = 1;
|
||||
$baseParams['__YKVAL_SYNC_SECURE_LEVEL__'] = 40;
|
||||
$baseParams['__YKVAL_SYNC_DEFAULT_LEVEL__'] = 60;
|
||||
$baseParams['__YKVAL_SYNC_DEFAULT_TIMEOUT__'] = 1;
|
||||
|
||||
// otp2ksmurls: Return array of YK-KSM URLs for decrypting OTP for
|
||||
// CLIENT. The URLs must be fully qualified, i.e., contain the OTP
|
||||
// itself.
|
||||
function otp2ksmurls ($otp, $client) {
|
||||
//if ($client == 42) {
|
||||
// return array("http://another-ykkms.example.com/wsapi/decrypt?otp=$otp");
|
||||
//}
|
||||
|
||||
//if (preg_match ("/^dteffujehknh/", $otp)) {
|
||||
// return array("http://different-ykkms.example.com/wsapi/decrypt?otp=$otp");
|
||||
//}
|
||||
|
||||
return array(
|
||||
//"http://ykkms1.example.com/wsapi/decrypt?otp=$otp",
|
||||
//"http://ykkms2.example.com/wsapi/decrypt?otp=$otp",
|
||||
"http://127.0.0.1/wsapi/decrypt?otp=$otp"
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
|
420
ykval-sync.php
420
ykval-sync.php
@ -1,210 +1,210 @@
|
||||
<?php
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'ykval-config.php';
|
||||
require_once 'ykval-synclib.php';
|
||||
|
||||
$apiKey = '';
|
||||
|
||||
header("content-type: text/plain");
|
||||
|
||||
|
||||
$myLog = new Log('ykval-sync');
|
||||
$myLog->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if(empty($_SERVER['QUERY_STRING'])) {
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$myLog->log(LOG_INFO, "Request: " . $_SERVER['QUERY_STRING']);
|
||||
|
||||
$sync = new SyncLib('ykval-sync:synclib');
|
||||
$sync->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if (! $sync->isConnected()) {
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
#
|
||||
# Verify that request comes from valid server
|
||||
#
|
||||
|
||||
$myLog->log(LOG_INFO, 'Received request from ' . $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$allowed = in_array($_SERVER['REMOTE_ADDR'], $baseParams['__YKVAL_ALLOWED_SYNC_POOL__']);
|
||||
|
||||
if (!$allowed) {
|
||||
$myLog->log(LOG_NOTICE, 'Operation not allowed from IP ' . $_SERVER['REMOTE_ADDR']);
|
||||
$myLog->log(LOG_DEBUG, 'Remote IP ' . $_SERVER['REMOTE_ADDR'] . ' not listed in allowed sync pool : ' .
|
||||
implode(', ', $baseParams['__YKVAL_ALLOWED_SYNC_POOL__']));
|
||||
sendResp(S_OPERATION_NOT_ALLOWED, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
#
|
||||
# Define requirements on protocol
|
||||
#
|
||||
|
||||
$syncParams=array('modified'=>Null,
|
||||
'otp'=>Null,
|
||||
'nonce'=>Null,
|
||||
'yk_publicname'=>Null,
|
||||
'yk_counter'=>Null,
|
||||
'yk_use'=>Null,
|
||||
'yk_high'=>Null,
|
||||
'yk_low'=>Null);
|
||||
|
||||
#
|
||||
# Extract values from HTTP request
|
||||
#
|
||||
|
||||
$tmp_log = "Received ";
|
||||
foreach ($syncParams as $param=>$value) {
|
||||
$value = getHttpVal($param, Null);
|
||||
if ($value==Null) {
|
||||
$myLog->log(LOG_NOTICE, "Received request with parameter[s] (" . $param . ") missing value");
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
$syncParams[$param]=$value;
|
||||
$tmp_log .= "$param=$value ";
|
||||
}
|
||||
$myLog->log(LOG_INFO, $tmp_log);
|
||||
|
||||
#
|
||||
# At this point we should have the otp so let's add it to the logging module
|
||||
#
|
||||
$myLog->addField('otp', $syncParams['otp']);
|
||||
$sync->addField('otp', $syncParams['otp']);
|
||||
|
||||
#
|
||||
# Verify correctness of input parameters
|
||||
#
|
||||
|
||||
foreach (array('modified') as $param) {
|
||||
if (preg_match("/^[0-9]+$/", $syncParams[$param])==0) {
|
||||
$myLog->log(LOG_NOTICE, 'Input parameters ' . $param . ' not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('yk_counter', 'yk_use', 'yk_high', 'yk_low') as $param) {
|
||||
if (preg_match("/^(-1|[0-9]+)$/", $syncParams[$param])==0) {
|
||||
$myLog->log(LOG_NOTICE, 'Input parameters ' . $param . ' not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Get local counter data
|
||||
#
|
||||
|
||||
$yk_publicname = $syncParams['yk_publicname'];
|
||||
$localParams = $sync->getLocalParams($yk_publicname);
|
||||
if (!$localParams) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid Yubikey ' . $yk_publicname);
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Conditional update local database */
|
||||
$sync->updateDbCounters($syncParams);
|
||||
|
||||
$myLog->log(LOG_DEBUG, 'Local params ' , $localParams);
|
||||
$myLog->log(LOG_DEBUG, 'Sync request params ' , $syncParams);
|
||||
|
||||
#
|
||||
# Compare sync and local counters and generate warnings according to
|
||||
#
|
||||
# https://github.com/Yubico/yubikey-val/wiki/ServerReplicationProtocol
|
||||
#
|
||||
|
||||
|
||||
|
||||
if ($sync->countersHigherThan($localParams, $syncParams)) {
|
||||
$myLog->log(LOG_WARNING, 'Remote server out of sync.');
|
||||
}
|
||||
|
||||
|
||||
if ($sync->countersEqual($localParams, $syncParams)) {
|
||||
|
||||
if ($syncParams['modified']==$localParams['modified'] &&
|
||||
$syncParams['nonce']==$localParams['nonce']) {
|
||||
/* This is not an error. When the remote server received an OTP to verify, it would
|
||||
* have sent out sync requests immediately. When the required number of responses had
|
||||
* been received, the current implementation discards all additional responses (to
|
||||
* return the result to the client as soon as possible). If our response sent last
|
||||
* time was discarded, we will end up here when the background ykval-queue processes
|
||||
* the sync request again.
|
||||
*/
|
||||
$myLog->log(LOG_INFO, 'Sync request unnecessarily sent');
|
||||
}
|
||||
|
||||
if ($syncParams['modified']!=$localParams['modified'] &&
|
||||
$syncParams['nonce']==$localParams['nonce']) {
|
||||
$deltaModified = $syncParams['modified'] - $localParams['modified'];
|
||||
if($deltaModified < -1 || $deltaModified > 1) {
|
||||
$myLog->log(LOG_WARNING, 'We might have a replay. 2 events at different times have generated the same counters. The time difference is ' . $deltaModified . ' seconds');
|
||||
}
|
||||
}
|
||||
|
||||
if ($syncParams['nonce']!=$localParams['nonce']) {
|
||||
$myLog->log(LOG_WARNING, 'Remote server has received a request to validate an already validated OTP ');
|
||||
}
|
||||
}
|
||||
|
||||
if ($localParams['active'] != 1) {
|
||||
/* The remote server has accepted an OTP from a YubiKey which we would not.
|
||||
* We still needed to update our counters with the counters from the OTP though.
|
||||
*/
|
||||
$myLog->log(LOG_WARNING, 'Received sync-request for de-activated Yubikey ' . $yk_publicname .
|
||||
' - check database synchronization!!!');
|
||||
sendResp(S_BAD_OTP, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$extra=array('modified'=>$localParams['modified'],
|
||||
'nonce'=>$localParams['nonce'],
|
||||
'yk_publicname'=>$yk_publicname,
|
||||
'yk_counter'=>$localParams['yk_counter'],
|
||||
'yk_use'=>$localParams['yk_use'],
|
||||
'yk_high'=>$localParams['yk_high'],
|
||||
'yk_low'=>$localParams['yk_low']);
|
||||
|
||||
sendResp(S_OK, $myLog, $apiKey, $extra);
|
||||
|
||||
?>
|
||||
<?php
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'ykval-config.php';
|
||||
require_once 'ykval-synclib.php';
|
||||
|
||||
$apiKey = '';
|
||||
|
||||
header("content-type: text/plain");
|
||||
|
||||
|
||||
$myLog = new Log('ykval-sync');
|
||||
$myLog->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if(empty($_SERVER['QUERY_STRING'])) {
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$myLog->log(LOG_INFO, "Request: " . $_SERVER['QUERY_STRING']);
|
||||
|
||||
$sync = new SyncLib('ykval-sync:synclib');
|
||||
$sync->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
if (! $sync->isConnected()) {
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
#
|
||||
# Verify that request comes from valid server
|
||||
#
|
||||
|
||||
$myLog->log(LOG_INFO, 'Received request from ' . $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$allowed = in_array($_SERVER['REMOTE_ADDR'], $baseParams['__YKVAL_ALLOWED_SYNC_POOL__']);
|
||||
|
||||
if (!$allowed) {
|
||||
$myLog->log(LOG_NOTICE, 'Operation not allowed from IP ' . $_SERVER['REMOTE_ADDR']);
|
||||
$myLog->log(LOG_DEBUG, 'Remote IP ' . $_SERVER['REMOTE_ADDR'] . ' not listed in allowed sync pool : ' .
|
||||
implode(', ', $baseParams['__YKVAL_ALLOWED_SYNC_POOL__']));
|
||||
sendResp(S_OPERATION_NOT_ALLOWED, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
#
|
||||
# Define requirements on protocol
|
||||
#
|
||||
|
||||
$syncParams=array('modified'=>Null,
|
||||
'otp'=>Null,
|
||||
'nonce'=>Null,
|
||||
'yk_publicname'=>Null,
|
||||
'yk_counter'=>Null,
|
||||
'yk_use'=>Null,
|
||||
'yk_high'=>Null,
|
||||
'yk_low'=>Null);
|
||||
|
||||
#
|
||||
# Extract values from HTTP request
|
||||
#
|
||||
|
||||
$tmp_log = "Received ";
|
||||
foreach ($syncParams as $param=>$value) {
|
||||
$value = getHttpVal($param, Null);
|
||||
if ($value==Null) {
|
||||
$myLog->log(LOG_NOTICE, "Received request with parameter[s] (" . $param . ") missing value");
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
$syncParams[$param]=$value;
|
||||
$tmp_log .= "$param=$value ";
|
||||
}
|
||||
$myLog->log(LOG_INFO, $tmp_log);
|
||||
|
||||
#
|
||||
# At this point we should have the otp so let's add it to the logging module
|
||||
#
|
||||
$myLog->addField('otp', $syncParams['otp']);
|
||||
$sync->addField('otp', $syncParams['otp']);
|
||||
|
||||
#
|
||||
# Verify correctness of input parameters
|
||||
#
|
||||
|
||||
foreach (array('modified') as $param) {
|
||||
if (preg_match("/^[0-9]+$/", $syncParams[$param])==0) {
|
||||
$myLog->log(LOG_NOTICE, 'Input parameters ' . $param . ' not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('yk_counter', 'yk_use', 'yk_high', 'yk_low') as $param) {
|
||||
if (preg_match("/^(-1|[0-9]+)$/", $syncParams[$param])==0) {
|
||||
$myLog->log(LOG_NOTICE, 'Input parameters ' . $param . ' not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Get local counter data
|
||||
#
|
||||
|
||||
$yk_publicname = $syncParams['yk_publicname'];
|
||||
$localParams = $sync->getLocalParams($yk_publicname);
|
||||
if (!$localParams) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid Yubikey ' . $yk_publicname);
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Conditional update local database */
|
||||
$sync->updateDbCounters($syncParams);
|
||||
|
||||
$myLog->log(LOG_DEBUG, 'Local params ' , $localParams);
|
||||
$myLog->log(LOG_DEBUG, 'Sync request params ' , $syncParams);
|
||||
|
||||
#
|
||||
# Compare sync and local counters and generate warnings according to
|
||||
#
|
||||
# https://github.com/Yubico/yubikey-val/wiki/ServerReplicationProtocol
|
||||
#
|
||||
|
||||
|
||||
|
||||
if ($sync->countersHigherThan($localParams, $syncParams)) {
|
||||
$myLog->log(LOG_WARNING, 'Remote server out of sync.');
|
||||
}
|
||||
|
||||
|
||||
if ($sync->countersEqual($localParams, $syncParams)) {
|
||||
|
||||
if ($syncParams['modified']==$localParams['modified'] &&
|
||||
$syncParams['nonce']==$localParams['nonce']) {
|
||||
/* This is not an error. When the remote server received an OTP to verify, it would
|
||||
* have sent out sync requests immediately. When the required number of responses had
|
||||
* been received, the current implementation discards all additional responses (to
|
||||
* return the result to the client as soon as possible). If our response sent last
|
||||
* time was discarded, we will end up here when the background ykval-queue processes
|
||||
* the sync request again.
|
||||
*/
|
||||
$myLog->log(LOG_INFO, 'Sync request unnecessarily sent');
|
||||
}
|
||||
|
||||
if ($syncParams['modified']!=$localParams['modified'] &&
|
||||
$syncParams['nonce']==$localParams['nonce']) {
|
||||
$deltaModified = $syncParams['modified'] - $localParams['modified'];
|
||||
if($deltaModified < -1 || $deltaModified > 1) {
|
||||
$myLog->log(LOG_WARNING, 'We might have a replay. 2 events at different times have generated the same counters. The time difference is ' . $deltaModified . ' seconds');
|
||||
}
|
||||
}
|
||||
|
||||
if ($syncParams['nonce']!=$localParams['nonce']) {
|
||||
$myLog->log(LOG_WARNING, 'Remote server has received a request to validate an already validated OTP ');
|
||||
}
|
||||
}
|
||||
|
||||
if ($localParams['active'] != 1) {
|
||||
/* The remote server has accepted an OTP from a YubiKey which we would not.
|
||||
* We still needed to update our counters with the counters from the OTP though.
|
||||
*/
|
||||
$myLog->log(LOG_WARNING, 'Received sync-request for de-activated Yubikey ' . $yk_publicname .
|
||||
' - check database synchronization!!!');
|
||||
sendResp(S_BAD_OTP, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$extra=array('modified'=>$localParams['modified'],
|
||||
'nonce'=>$localParams['nonce'],
|
||||
'yk_publicname'=>$yk_publicname,
|
||||
'yk_counter'=>$localParams['yk_counter'],
|
||||
'yk_use'=>$localParams['yk_use'],
|
||||
'yk_high'=>$localParams['yk_high'],
|
||||
'yk_low'=>$localParams['yk_low']);
|
||||
|
||||
sendResp(S_OK, $myLog, $apiKey, $extra);
|
||||
|
||||
?>
|
||||
|
844
ykval-verify.php
844
ykval-verify.php
@ -1,422 +1,422 @@
|
||||
<?php
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'ykval-config.php';
|
||||
require_once 'ykval-synclib.php';
|
||||
|
||||
$apiKey = '';
|
||||
|
||||
header("content-type: text/plain");
|
||||
|
||||
$myLog = new Log('ykval-verify');
|
||||
$myLog->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
$query_string = '';
|
||||
if ($_POST) {
|
||||
$kv = array();
|
||||
foreach ($_POST as $key => $value) {
|
||||
$kv[] = "$key=$value";
|
||||
}
|
||||
$query_string = "POST: " . join("&", $kv);
|
||||
} else {
|
||||
$query_string = "Request: " . $_SERVER['QUERY_STRING'];
|
||||
}
|
||||
|
||||
$myLog->log(LOG_INFO, $query_string .
|
||||
" (at " . date("c") . " " . microtime() . ") " .
|
||||
(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on" ? "HTTPS" : "HTTP"));
|
||||
|
||||
/* Detect protocol version */
|
||||
if (preg_match("/\/wsapi\/([0-9]+)\.([0-9]+)\//", $_SERVER['REQUEST_URI'], $out)) {
|
||||
$protocol_version=$out[1]+$out[2]*0.1;
|
||||
} else {
|
||||
$protocol_version=1.0;
|
||||
}
|
||||
|
||||
$myLog->log(LOG_DEBUG, "found protocol version " . $protocol_version);
|
||||
|
||||
/* Extract values from HTTP request
|
||||
*/
|
||||
$h = getHttpVal('h', '');
|
||||
$client = getHttpVal('id', 0);
|
||||
$otp = getHttpVal('otp', '');
|
||||
$otp = strtolower($otp);
|
||||
if (preg_match("/^[jxe.uidchtnbpygk]+$/", $otp)) {
|
||||
$new_otp = strtr($otp, "jxe.uidchtnbpygk", "cbdefghijklnrtuv");
|
||||
$myLog->log(LOG_INFO, 'Dvorak OTP converting ' . $otp . ' to ' . $new_otp);
|
||||
$otp = $new_otp;
|
||||
}
|
||||
$timestamp = getHttpVal('timestamp', 0);
|
||||
|
||||
/* Construct response parameters */
|
||||
$extra=array();
|
||||
if ($protocol_version>=2.0) {
|
||||
$extra['otp']=$otp;
|
||||
}
|
||||
|
||||
|
||||
/* We have the OTP now, so let's add it to the logging */
|
||||
$myLog->addField('otp', $otp);
|
||||
|
||||
if ($protocol_version>=2.0) {
|
||||
$sl = getHttpVal('sl', '');
|
||||
$timeout = getHttpVal('timeout', '');
|
||||
$nonce = getHttpVal('nonce', '');
|
||||
|
||||
/* Add nonce to response parameters */
|
||||
$extra['nonce']= $nonce;
|
||||
|
||||
/* Nonce is required from protocol 2.0 */
|
||||
if(!$nonce) {
|
||||
$myLog->log(LOG_NOTICE, 'Nonce is missing and protocol version >= 2.0');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sanity check HTTP parameters
|
||||
|
||||
* otp: one-time password
|
||||
* id: client id
|
||||
* timeout: timeout in seconds to wait for external answers, optional: if absent the server decides
|
||||
* nonce: random alphanumeric string, 16 to 40 characters long. Must be non-predictable and changing for each request, but need not be cryptographically strong
|
||||
* sl: "sync level", percentage of external servers that needs to answer (integer 0 to 100), or "fast" or "secure" to use server-configured values
|
||||
* h: signature (optional)
|
||||
* timestamp: requests timestamp/counters in response
|
||||
|
||||
*/
|
||||
|
||||
/* Change default protocol "strings" to numeric values */
|
||||
if (isset($sl) && strcasecmp($sl, 'fast')==0) {
|
||||
$sl=$baseParams['__YKVAL_SYNC_FAST_LEVEL__'];
|
||||
}
|
||||
if (isset($sl) && strcasecmp($sl, 'secure')==0) {
|
||||
$sl=$baseParams['__YKVAL_SYNC_SECURE_LEVEL__'];
|
||||
}
|
||||
if (!isset($sl) || $sl == '') {
|
||||
$sl=$baseParams['__YKVAL_SYNC_DEFAULT_LEVEL__'];
|
||||
}
|
||||
if (!isset($timeout) || $timeout == '') {
|
||||
$timeout=$baseParams['__YKVAL_SYNC_DEFAULT_TIMEOUT__'];
|
||||
}
|
||||
|
||||
if ($otp == '') {
|
||||
$myLog->log(LOG_NOTICE, 'OTP is missing');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (strlen($otp) < TOKEN_LEN || strlen ($otp) > OTP_MAX_LEN) {
|
||||
$myLog->log(LOG_NOTICE, 'Incorrect OTP length: ' . $otp);
|
||||
sendResp(S_BAD_OTP, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (preg_match("/^[cbdefghijklnrtuv]+$/", $otp)==0) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid OTP: ' . $otp);
|
||||
sendResp(S_BAD_OTP, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (preg_match("/^[0-9]+$/", $client)==0){
|
||||
$myLog->log(LOG_NOTICE, 'id provided in request must be an integer');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($timeout && preg_match("/^[0-9]+$/", $timeout)==0) {
|
||||
$myLog->log(LOG_NOTICE, 'timeout is provided but not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($nonce) && preg_match("/^[A-Za-z0-9]+$/", $nonce)==0) {
|
||||
$myLog->log(LOG_NOTICE, 'NONCE is provided but not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($nonce) && (strlen($nonce) < 16 || strlen($nonce) > 40)) {
|
||||
$myLog->log(LOG_NOTICE, 'Nonce too short or too long');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($sl && (preg_match("/^[0-9]+$/", $sl)==0 || ($sl<0 || $sl>100))) {
|
||||
$myLog->log(LOG_NOTICE, 'SL is provided but not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
// NOTE: Timestamp parameter is not checked since current protocol says that 1 means request timestamp
|
||||
// and anything else is discarded.
|
||||
|
||||
//// Get Client info from DB
|
||||
//
|
||||
if ($client <= 0) {
|
||||
$myLog->log(LOG_NOTICE, 'Client ID is missing');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Initialize the sync library. Strive to use this instead of custom
|
||||
DB requests, custom comparisons etc */
|
||||
$sync = new SyncLib('ykval-verify:synclib');
|
||||
$sync->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
$sync->addField('otp', $otp);
|
||||
|
||||
if (! $sync->isConnected()) {
|
||||
sendResp(S_BACKEND_ERROR, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
$cd=$sync->getClientData($client);
|
||||
if(!$cd) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid client id ' . $client);
|
||||
sendResp(S_NO_SUCH_CLIENT, $myLog);
|
||||
exit;
|
||||
}
|
||||
$myLog->log(LOG_DEBUG,"Client data:", $cd);
|
||||
|
||||
//// Check client signature
|
||||
//
|
||||
$apiKey = base64_decode($cd['secret']);
|
||||
|
||||
if ($h != '') {
|
||||
// Create the signature using the API key
|
||||
$a;
|
||||
if($_GET) {
|
||||
$a = $_GET;
|
||||
} elseif($_POST) {
|
||||
$a = $_POST;
|
||||
} else {
|
||||
sendRest(S_BACKEND_ERROR);
|
||||
exit;
|
||||
}
|
||||
unset($a['h']);
|
||||
|
||||
$hmac = sign($a, $apiKey, $myLog);
|
||||
// Compare it
|
||||
if ($hmac != $h) {
|
||||
$myLog->log(LOG_DEBUG, 'client hmac=' . $h . ', server hmac=' . $hmac);
|
||||
sendResp(S_BAD_SIGNATURE, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to add necessary parameters not available at earlier protocols after signature is computed.
|
||||
*/
|
||||
if ($protocol_version<2.0) {
|
||||
/* We need to create a nonce manually here */
|
||||
$nonce = md5(uniqid(rand()));
|
||||
$myLog->log(LOG_INFO, 'protocol version below 2.0. Created nonce ' . $nonce);
|
||||
}
|
||||
|
||||
//// Which YK-KSM should we talk to?
|
||||
//
|
||||
$urls = otp2ksmurls ($otp, $client);
|
||||
if (!is_array($urls)) {
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
//// Decode OTP from input
|
||||
//
|
||||
$otpinfo = KSMdecryptOTP($urls, $myLog);
|
||||
if (!is_array($otpinfo)) {
|
||||
sendResp(S_BAD_OTP, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
$myLog->log(LOG_DEBUG, "Decrypted OTP:", $otpinfo);
|
||||
|
||||
//// Get Yubikey from DB
|
||||
//
|
||||
$devId = substr($otp, 0, strlen ($otp) - TOKEN_LEN);
|
||||
$yk_publicname=$devId;
|
||||
$localParams = $sync->getLocalParams($yk_publicname);
|
||||
if (!$localParams) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid Yubikey ' . $yk_publicname);
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$myLog->log(LOG_DEBUG, "Auth data:", $localParams);
|
||||
if ($localParams['active'] != 1) {
|
||||
$myLog->log(LOG_NOTICE, 'De-activated Yubikey ' . $devId);
|
||||
sendResp(S_BAD_OTP, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Build OTP params */
|
||||
|
||||
$otpParams=array('modified'=>time(),
|
||||
'otp'=>$otp,
|
||||
'nonce'=>$nonce,
|
||||
'yk_publicname'=>$devId,
|
||||
'yk_counter'=>$otpinfo['session_counter'],
|
||||
'yk_use'=>$otpinfo['session_use'],
|
||||
'yk_high'=>$otpinfo['high'],
|
||||
'yk_low'=>$otpinfo['low']);
|
||||
|
||||
|
||||
/* First check if OTP is seen with the same nonce, in such case we have an replayed request */
|
||||
if ($sync->countersEqual($localParams, $otpParams) &&
|
||||
$localParams['nonce']==$otpParams['nonce']) {
|
||||
$myLog->log(LOG_WARNING, 'Replayed request');
|
||||
sendResp(S_REPLAYED_REQUEST, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Check the OTP counters against local db */
|
||||
if ($sync->countersHigherThanOrEqual($localParams, $otpParams)) {
|
||||
$sync->log(LOG_WARNING, 'replayed OTP: Local counters higher');
|
||||
$sync->log(LOG_WARNING, 'replayed OTP: Local counters ', $localParams);
|
||||
$sync->log(LOG_WARNING, 'replayed OTP: Otp counters ', $otpParams);
|
||||
sendResp(S_REPLAYED_OTP, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Valid OTP, update database. */
|
||||
|
||||
if(!$sync->updateDbCounters($otpParams)) {
|
||||
$myLog->log(LOG_CRIT, "Failed to update yubikey counters in database");
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Queue sync requests */
|
||||
|
||||
if (!$sync->queue($otpParams, $localParams)) {
|
||||
$myLog->log(LOG_CRIT, "ykval-verify:critical:failed to queue sync requests");
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$nr_servers=$sync->getNumberOfServers();
|
||||
$req_answers=ceil($nr_servers*$sl/100.0);
|
||||
if ($req_answers>0) {
|
||||
$syncres=$sync->sync($req_answers, $timeout);
|
||||
$nr_answers=$sync->getNumberOfAnswers();
|
||||
$nr_valid_answers=$sync->getNumberOfValidAnswers();
|
||||
$sl_success_rate=floor(100.0 * $nr_valid_answers / $nr_servers);
|
||||
|
||||
} else {
|
||||
$syncres=true;
|
||||
$nr_answers=0;
|
||||
$nr_valid_answers=0;
|
||||
$sl_success_rate=0;
|
||||
}
|
||||
$myLog->log(LOG_INFO, "ykval-verify:notice:synclevel=" . $sl .
|
||||
" nr servers=" . $nr_servers .
|
||||
" req answers=" . $req_answers .
|
||||
" answers=" . $nr_answers .
|
||||
" valid answers=" . $nr_valid_answers .
|
||||
" sl success rate=" . $sl_success_rate .
|
||||
" timeout=" . $timeout);
|
||||
|
||||
if($syncres==False) {
|
||||
/* sync returned false, indicating that
|
||||
either at least 1 answer marked OTP as invalid or
|
||||
there were not enough answers */
|
||||
$myLog->log(LOG_WARNING, "ykval-verify:notice:Sync failed");
|
||||
if ($nr_valid_answers!=$nr_answers) {
|
||||
sendResp(S_REPLAYED_OTP, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
} else {
|
||||
$extra['sl']=$sl_success_rate;
|
||||
sendResp(S_NOT_ENOUGH_ANSWERS, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Recreate parameters to make phising test work out
|
||||
TODO: use timefunctionality in deltatime library instead */
|
||||
$sessionCounter = $otpParams['yk_counter'];
|
||||
$sessionUse = $otpParams['yk_use'];
|
||||
$seenSessionCounter = $localParams['yk_counter'];
|
||||
$seenSessionUse = $localParams['yk_use'];
|
||||
|
||||
$ad['high']=$localParams['yk_high'];
|
||||
$ad['low']=$localParams['yk_low'];
|
||||
$ad['accessed']=$sync->unixToDbTime($localParams['modified']);
|
||||
|
||||
//// Check the time stamp
|
||||
//
|
||||
if ($sessionCounter == $seenSessionCounter && $sessionUse > $seenSessionUse) {
|
||||
$ts = ($otpinfo['high'] << 16) + $otpinfo['low'];
|
||||
$seenTs = ($ad['high'] << 16) + $ad['low'];
|
||||
$tsDiff = $ts - $seenTs;
|
||||
$tsDelta = $tsDiff * TS_SEC;
|
||||
|
||||
//// Check the real time
|
||||
//
|
||||
$lastTime = strtotime($ad['accessed']);
|
||||
$now = time();
|
||||
$elapsed = $now - $lastTime;
|
||||
$deviation = abs($elapsed - $tsDelta);
|
||||
|
||||
// Time delta server might verify multiple OTPS in a row. In such case validation server doesn't
|
||||
// have time to tick a whole second and we need to avoid division by zero.
|
||||
if ($elapsed != 0) {
|
||||
$percent = $deviation/$elapsed;
|
||||
} else {
|
||||
$percent = 1;
|
||||
}
|
||||
$myLog->log(LOG_INFO, "Timestamp seen=" . $seenTs . " this=" . $ts .
|
||||
" delta=" . $tsDiff . ' secs=' . $tsDelta .
|
||||
' accessed=' . $lastTime .' (' . $ad['accessed'] . ') now='
|
||||
. $now . ' (' . strftime("%Y-%m-%d %H:%M:%S", $now)
|
||||
. ') elapsed=' . $elapsed .
|
||||
' deviation=' . $deviation . ' secs or '.
|
||||
round(100*$percent) . '%');
|
||||
if ($deviation > TS_ABS_TOLERANCE && $percent > TS_REL_TOLERANCE) {
|
||||
$myLog->log(LOG_NOTICE, "OTP failed phishing test");
|
||||
if (0) {
|
||||
sendResp(S_DELAYED_OTP, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill up with more respone parameters */
|
||||
if ($protocol_version>=2.0) {
|
||||
$extra['sl'] = $sl_success_rate;
|
||||
}
|
||||
if ($timestamp==1){
|
||||
$extra['timestamp'] = ($otpinfo['high'] << 16) + $otpinfo['low'];
|
||||
$extra['sessioncounter'] = $sessionCounter;
|
||||
$extra['sessionuse'] = $sessionUse;
|
||||
}
|
||||
|
||||
sendResp(S_OK, $myLog, $apiKey, $extra);
|
||||
|
||||
?>
|
||||
<?php
|
||||
|
||||
# Copyright (c) 2009-2013 Yubico AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'ykval-config.php';
|
||||
require_once 'ykval-synclib.php';
|
||||
|
||||
$apiKey = '';
|
||||
|
||||
header("content-type: text/plain");
|
||||
|
||||
$myLog = new Log('ykval-verify');
|
||||
$myLog->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
$query_string = '';
|
||||
if ($_POST) {
|
||||
$kv = array();
|
||||
foreach ($_POST as $key => $value) {
|
||||
$kv[] = "$key=$value";
|
||||
}
|
||||
$query_string = "POST: " . join("&", $kv);
|
||||
} else {
|
||||
$query_string = "Request: " . $_SERVER['QUERY_STRING'];
|
||||
}
|
||||
|
||||
$myLog->log(LOG_INFO, $query_string .
|
||||
" (at " . date("c") . " " . microtime() . ") " .
|
||||
(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on" ? "HTTPS" : "HTTP"));
|
||||
|
||||
/* Detect protocol version */
|
||||
if (preg_match("/\/wsapi\/([0-9]+)\.([0-9]+)\//", $_SERVER['REQUEST_URI'], $out)) {
|
||||
$protocol_version=$out[1]+$out[2]*0.1;
|
||||
} else {
|
||||
$protocol_version=1.0;
|
||||
}
|
||||
|
||||
$myLog->log(LOG_DEBUG, "found protocol version " . $protocol_version);
|
||||
|
||||
/* Extract values from HTTP request
|
||||
*/
|
||||
$h = getHttpVal('h', '');
|
||||
$client = getHttpVal('id', 0);
|
||||
$otp = getHttpVal('otp', '');
|
||||
$otp = strtolower($otp);
|
||||
if (preg_match("/^[jxe.uidchtnbpygk]+$/", $otp)) {
|
||||
$new_otp = strtr($otp, "jxe.uidchtnbpygk", "cbdefghijklnrtuv");
|
||||
$myLog->log(LOG_INFO, 'Dvorak OTP converting ' . $otp . ' to ' . $new_otp);
|
||||
$otp = $new_otp;
|
||||
}
|
||||
$timestamp = getHttpVal('timestamp', 0);
|
||||
|
||||
/* Construct response parameters */
|
||||
$extra=array();
|
||||
if ($protocol_version>=2.0) {
|
||||
$extra['otp']=$otp;
|
||||
}
|
||||
|
||||
|
||||
/* We have the OTP now, so let's add it to the logging */
|
||||
$myLog->addField('otp', $otp);
|
||||
|
||||
if ($protocol_version>=2.0) {
|
||||
$sl = getHttpVal('sl', '');
|
||||
$timeout = getHttpVal('timeout', '');
|
||||
$nonce = getHttpVal('nonce', '');
|
||||
|
||||
/* Add nonce to response parameters */
|
||||
$extra['nonce']= $nonce;
|
||||
|
||||
/* Nonce is required from protocol 2.0 */
|
||||
if(!$nonce) {
|
||||
$myLog->log(LOG_NOTICE, 'Nonce is missing and protocol version >= 2.0');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Sanity check HTTP parameters
|
||||
|
||||
* otp: one-time password
|
||||
* id: client id
|
||||
* timeout: timeout in seconds to wait for external answers, optional: if absent the server decides
|
||||
* nonce: random alphanumeric string, 16 to 40 characters long. Must be non-predictable and changing for each request, but need not be cryptographically strong
|
||||
* sl: "sync level", percentage of external servers that needs to answer (integer 0 to 100), or "fast" or "secure" to use server-configured values
|
||||
* h: signature (optional)
|
||||
* timestamp: requests timestamp/counters in response
|
||||
|
||||
*/
|
||||
|
||||
/* Change default protocol "strings" to numeric values */
|
||||
if (isset($sl) && strcasecmp($sl, 'fast')==0) {
|
||||
$sl=$baseParams['__YKVAL_SYNC_FAST_LEVEL__'];
|
||||
}
|
||||
if (isset($sl) && strcasecmp($sl, 'secure')==0) {
|
||||
$sl=$baseParams['__YKVAL_SYNC_SECURE_LEVEL__'];
|
||||
}
|
||||
if (!isset($sl) || $sl == '') {
|
||||
$sl=$baseParams['__YKVAL_SYNC_DEFAULT_LEVEL__'];
|
||||
}
|
||||
if (!isset($timeout) || $timeout == '') {
|
||||
$timeout=$baseParams['__YKVAL_SYNC_DEFAULT_TIMEOUT__'];
|
||||
}
|
||||
|
||||
if ($otp == '') {
|
||||
$myLog->log(LOG_NOTICE, 'OTP is missing');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (strlen($otp) < TOKEN_LEN || strlen ($otp) > OTP_MAX_LEN) {
|
||||
$myLog->log(LOG_NOTICE, 'Incorrect OTP length: ' . $otp);
|
||||
sendResp(S_BAD_OTP, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (preg_match("/^[cbdefghijklnrtuv]+$/", $otp)==0) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid OTP: ' . $otp);
|
||||
sendResp(S_BAD_OTP, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (preg_match("/^[0-9]+$/", $client)==0){
|
||||
$myLog->log(LOG_NOTICE, 'id provided in request must be an integer');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($timeout && preg_match("/^[0-9]+$/", $timeout)==0) {
|
||||
$myLog->log(LOG_NOTICE, 'timeout is provided but not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($nonce) && preg_match("/^[A-Za-z0-9]+$/", $nonce)==0) {
|
||||
$myLog->log(LOG_NOTICE, 'NONCE is provided but not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($nonce) && (strlen($nonce) < 16 || strlen($nonce) > 40)) {
|
||||
$myLog->log(LOG_NOTICE, 'Nonce too short or too long');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($sl && (preg_match("/^[0-9]+$/", $sl)==0 || ($sl<0 || $sl>100))) {
|
||||
$myLog->log(LOG_NOTICE, 'SL is provided but not correct');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
// NOTE: Timestamp parameter is not checked since current protocol says that 1 means request timestamp
|
||||
// and anything else is discarded.
|
||||
|
||||
//// Get Client info from DB
|
||||
//
|
||||
if ($client <= 0) {
|
||||
$myLog->log(LOG_NOTICE, 'Client ID is missing');
|
||||
sendResp(S_MISSING_PARAMETER, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Initialize the sync library. Strive to use this instead of custom
|
||||
DB requests, custom comparisons etc */
|
||||
$sync = new SyncLib('ykval-verify:synclib');
|
||||
$sync->addField('ip', $_SERVER['REMOTE_ADDR']);
|
||||
$sync->addField('otp', $otp);
|
||||
|
||||
if (! $sync->isConnected()) {
|
||||
sendResp(S_BACKEND_ERROR, $myLog);
|
||||
exit;
|
||||
}
|
||||
|
||||
$cd=$sync->getClientData($client);
|
||||
if(!$cd) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid client id ' . $client);
|
||||
sendResp(S_NO_SUCH_CLIENT, $myLog);
|
||||
exit;
|
||||
}
|
||||
$myLog->log(LOG_DEBUG,"Client data:", $cd);
|
||||
|
||||
//// Check client signature
|
||||
//
|
||||
$apiKey = base64_decode($cd['secret']);
|
||||
|
||||
if ($h != '') {
|
||||
// Create the signature using the API key
|
||||
$a;
|
||||
if($_GET) {
|
||||
$a = $_GET;
|
||||
} elseif($_POST) {
|
||||
$a = $_POST;
|
||||
} else {
|
||||
sendRest(S_BACKEND_ERROR);
|
||||
exit;
|
||||
}
|
||||
unset($a['h']);
|
||||
|
||||
$hmac = sign($a, $apiKey, $myLog);
|
||||
// Compare it
|
||||
if ($hmac != $h) {
|
||||
$myLog->log(LOG_DEBUG, 'client hmac=' . $h . ', server hmac=' . $hmac);
|
||||
sendResp(S_BAD_SIGNATURE, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to add necessary parameters not available at earlier protocols after signature is computed.
|
||||
*/
|
||||
if ($protocol_version<2.0) {
|
||||
/* We need to create a nonce manually here */
|
||||
$nonce = md5(uniqid(rand()));
|
||||
$myLog->log(LOG_INFO, 'protocol version below 2.0. Created nonce ' . $nonce);
|
||||
}
|
||||
|
||||
//// Which YK-KSM should we talk to?
|
||||
//
|
||||
$urls = otp2ksmurls ($otp, $client);
|
||||
if (!is_array($urls)) {
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
//// Decode OTP from input
|
||||
//
|
||||
$otpinfo = KSMdecryptOTP($urls, $myLog);
|
||||
if (!is_array($otpinfo)) {
|
||||
sendResp(S_BAD_OTP, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
$myLog->log(LOG_DEBUG, "Decrypted OTP:", $otpinfo);
|
||||
|
||||
//// Get Yubikey from DB
|
||||
//
|
||||
$devId = substr($otp, 0, strlen ($otp) - TOKEN_LEN);
|
||||
$yk_publicname=$devId;
|
||||
$localParams = $sync->getLocalParams($yk_publicname);
|
||||
if (!$localParams) {
|
||||
$myLog->log(LOG_NOTICE, 'Invalid Yubikey ' . $yk_publicname);
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$myLog->log(LOG_DEBUG, "Auth data:", $localParams);
|
||||
if ($localParams['active'] != 1) {
|
||||
$myLog->log(LOG_NOTICE, 'De-activated Yubikey ' . $devId);
|
||||
sendResp(S_BAD_OTP, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Build OTP params */
|
||||
|
||||
$otpParams=array('modified'=>time(),
|
||||
'otp'=>$otp,
|
||||
'nonce'=>$nonce,
|
||||
'yk_publicname'=>$devId,
|
||||
'yk_counter'=>$otpinfo['session_counter'],
|
||||
'yk_use'=>$otpinfo['session_use'],
|
||||
'yk_high'=>$otpinfo['high'],
|
||||
'yk_low'=>$otpinfo['low']);
|
||||
|
||||
|
||||
/* First check if OTP is seen with the same nonce, in such case we have an replayed request */
|
||||
if ($sync->countersEqual($localParams, $otpParams) &&
|
||||
$localParams['nonce']==$otpParams['nonce']) {
|
||||
$myLog->log(LOG_WARNING, 'Replayed request');
|
||||
sendResp(S_REPLAYED_REQUEST, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Check the OTP counters against local db */
|
||||
if ($sync->countersHigherThanOrEqual($localParams, $otpParams)) {
|
||||
$sync->log(LOG_WARNING, 'replayed OTP: Local counters higher');
|
||||
$sync->log(LOG_WARNING, 'replayed OTP: Local counters ', $localParams);
|
||||
$sync->log(LOG_WARNING, 'replayed OTP: Otp counters ', $otpParams);
|
||||
sendResp(S_REPLAYED_OTP, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Valid OTP, update database. */
|
||||
|
||||
if(!$sync->updateDbCounters($otpParams)) {
|
||||
$myLog->log(LOG_CRIT, "Failed to update yubikey counters in database");
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* Queue sync requests */
|
||||
|
||||
if (!$sync->queue($otpParams, $localParams)) {
|
||||
$myLog->log(LOG_CRIT, "ykval-verify:critical:failed to queue sync requests");
|
||||
sendResp(S_BACKEND_ERROR, $myLog, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
$nr_servers=$sync->getNumberOfServers();
|
||||
$req_answers=ceil($nr_servers*$sl/100.0);
|
||||
if ($req_answers>0) {
|
||||
$syncres=$sync->sync($req_answers, $timeout);
|
||||
$nr_answers=$sync->getNumberOfAnswers();
|
||||
$nr_valid_answers=$sync->getNumberOfValidAnswers();
|
||||
$sl_success_rate=floor(100.0 * $nr_valid_answers / $nr_servers);
|
||||
|
||||
} else {
|
||||
$syncres=true;
|
||||
$nr_answers=0;
|
||||
$nr_valid_answers=0;
|
||||
$sl_success_rate=0;
|
||||
}
|
||||
$myLog->log(LOG_INFO, "ykval-verify:notice:synclevel=" . $sl .
|
||||
" nr servers=" . $nr_servers .
|
||||
" req answers=" . $req_answers .
|
||||
" answers=" . $nr_answers .
|
||||
" valid answers=" . $nr_valid_answers .
|
||||
" sl success rate=" . $sl_success_rate .
|
||||
" timeout=" . $timeout);
|
||||
|
||||
if($syncres==False) {
|
||||
/* sync returned false, indicating that
|
||||
either at least 1 answer marked OTP as invalid or
|
||||
there were not enough answers */
|
||||
$myLog->log(LOG_WARNING, "ykval-verify:notice:Sync failed");
|
||||
if ($nr_valid_answers!=$nr_answers) {
|
||||
sendResp(S_REPLAYED_OTP, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
} else {
|
||||
$extra['sl']=$sl_success_rate;
|
||||
sendResp(S_NOT_ENOUGH_ANSWERS, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Recreate parameters to make phising test work out
|
||||
TODO: use timefunctionality in deltatime library instead */
|
||||
$sessionCounter = $otpParams['yk_counter'];
|
||||
$sessionUse = $otpParams['yk_use'];
|
||||
$seenSessionCounter = $localParams['yk_counter'];
|
||||
$seenSessionUse = $localParams['yk_use'];
|
||||
|
||||
$ad['high']=$localParams['yk_high'];
|
||||
$ad['low']=$localParams['yk_low'];
|
||||
$ad['accessed']=$sync->unixToDbTime($localParams['modified']);
|
||||
|
||||
//// Check the time stamp
|
||||
//
|
||||
if ($sessionCounter == $seenSessionCounter && $sessionUse > $seenSessionUse) {
|
||||
$ts = ($otpinfo['high'] << 16) + $otpinfo['low'];
|
||||
$seenTs = ($ad['high'] << 16) + $ad['low'];
|
||||
$tsDiff = $ts - $seenTs;
|
||||
$tsDelta = $tsDiff * TS_SEC;
|
||||
|
||||
//// Check the real time
|
||||
//
|
||||
$lastTime = strtotime($ad['accessed']);
|
||||
$now = time();
|
||||
$elapsed = $now - $lastTime;
|
||||
$deviation = abs($elapsed - $tsDelta);
|
||||
|
||||
// Time delta server might verify multiple OTPS in a row. In such case validation server doesn't
|
||||
// have time to tick a whole second and we need to avoid division by zero.
|
||||
if ($elapsed != 0) {
|
||||
$percent = $deviation/$elapsed;
|
||||
} else {
|
||||
$percent = 1;
|
||||
}
|
||||
$myLog->log(LOG_INFO, "Timestamp seen=" . $seenTs . " this=" . $ts .
|
||||
" delta=" . $tsDiff . ' secs=' . $tsDelta .
|
||||
' accessed=' . $lastTime .' (' . $ad['accessed'] . ') now='
|
||||
. $now . ' (' . strftime("%Y-%m-%d %H:%M:%S", $now)
|
||||
. ') elapsed=' . $elapsed .
|
||||
' deviation=' . $deviation . ' secs or '.
|
||||
round(100*$percent) . '%');
|
||||
if ($deviation > TS_ABS_TOLERANCE && $percent > TS_REL_TOLERANCE) {
|
||||
$myLog->log(LOG_NOTICE, "OTP failed phishing test");
|
||||
if (0) {
|
||||
sendResp(S_DELAYED_OTP, $myLog, $apiKey, $extra);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill up with more respone parameters */
|
||||
if ($protocol_version>=2.0) {
|
||||
$extra['sl'] = $sl_success_rate;
|
||||
}
|
||||
if ($timestamp==1){
|
||||
$extra['timestamp'] = ($otpinfo['high'] << 16) + $otpinfo['low'];
|
||||
$extra['sessioncounter'] = $sessionCounter;
|
||||
$extra['sessionuse'] = $sessionUse;
|
||||
}
|
||||
|
||||
sendResp(S_OK, $myLog, $apiKey, $extra);
|
||||
|
||||
?>
|
||||
|
Loading…
x
Reference in New Issue
Block a user