mirror of
https://github.com/Yubico/yubikey-val.git
synced 2025-03-16 03:29:18 +01:00
Committed first trial version for replication protocol.
This commit is contained in:
parent
682c1d94cd
commit
f04dcbc0e7
375
lib/Db.php
Normal file
375
lib/Db.php
Normal file
@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Class for managing database connection
|
||||
*
|
||||
* LICENSE:
|
||||
*
|
||||
* Copyright (c) 2009 Yubico. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* o 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.
|
||||
* o The names of the authors may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author Olov Danielson <olov.danielson@gmail.com>
|
||||
* @copyright 2009 Yubico
|
||||
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @link http://www.yubico.com/
|
||||
* @link http://code.google.com/p/yubikey-timedelta-server-php/
|
||||
*/
|
||||
class Db
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $host Database host
|
||||
* @param string $user Database user
|
||||
* @param string $pwd Database password
|
||||
* @param string $name Database table name
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
public function __construct($host, $user, $pwd, $db_name)
|
||||
{
|
||||
$this->host=$host;
|
||||
$this->user=$user;
|
||||
$this->pwd=$pwd;
|
||||
$this->db_name=$db_name;
|
||||
}
|
||||
/**
|
||||
* function to convert Db timestamps to unixtime(s)
|
||||
*
|
||||
* @param string $updated Database timestamp
|
||||
* @return int Timestamp in unixtime format
|
||||
*
|
||||
*/
|
||||
public function timestampToTime($updated)
|
||||
{
|
||||
$stamp=strptime($updated, '%F %H:%M:%S');
|
||||
return mktime($stamp[tm_hour], $stamp[tm_min], $stamp[tm_sec], $stamp[tm_mon]+1, $stamp[tm_mday], $stamp[tm_year]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* function to compute delta (s) between 2 Db timestamps
|
||||
*
|
||||
* @param string $first Database timestamp 1
|
||||
* @param string $second Database timestamp 2
|
||||
* @return int Deltatime (s)
|
||||
*
|
||||
*/
|
||||
public function timestampDeltaTime($first, $second)
|
||||
{
|
||||
return Db::timestampToTime($second) - Db::timestampToTime($first);
|
||||
}
|
||||
|
||||
/**
|
||||
* function to disconnect from database
|
||||
*
|
||||
* @return boolean True on success, otherwise false.
|
||||
*
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->db_conn!=NULL) {
|
||||
mysql_close($this->db_conn);
|
||||
$this->db_conn=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function to check if database is connected
|
||||
*
|
||||
* @return boolean True if connected, otherwise false.
|
||||
*
|
||||
*/
|
||||
public function isConnected()
|
||||
{
|
||||
if ($this->db_conn!=NULL) return True;
|
||||
else return False;
|
||||
}
|
||||
/**
|
||||
* function to connect to database defined in config.php
|
||||
*
|
||||
* @return boolean True on success, otherwise false.
|
||||
*
|
||||
*/
|
||||
public function connect(){
|
||||
if (! $this->db_conn = mysql_connect($this->host, $this->user, $this->pwd)) {
|
||||
echo 'Could not connect: ' . mysql_error();
|
||||
return false;
|
||||
}
|
||||
if (! mysql_select_db($this->db_name)) {
|
||||
echo 'Could not select database ' . $this->db_name;
|
||||
$this->disconnect();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function truncateTable($name)
|
||||
{
|
||||
mysql_query("TRUNCATE TABLE " . $name);
|
||||
}
|
||||
/**
|
||||
* function to update row in database
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param int $id Id on row to update
|
||||
* @param array $values Array with key=>values to update
|
||||
* @return boolean True on success, otherwise false.
|
||||
*
|
||||
*/
|
||||
public function update($table, $id, $values)
|
||||
{
|
||||
|
||||
foreach ($values as $key=>$value){
|
||||
if ($value != null) $query = $query . " " . $key . "='" . $value . "',";
|
||||
}
|
||||
if (! $query) {
|
||||
log("no values to set in query. Not updating DB");
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = rtrim($query, ",") . " WHERE id = " . $id;
|
||||
// Insert UPDATE statement at beginning
|
||||
$query = "UPDATE " . $table . " SET " . $query;
|
||||
if (! mysql_query($query)){
|
||||
echo 'Query failed: ' . mysql_error();
|
||||
echo 'Query was: ' . $query;
|
||||
error_log('Query failed: ' . mysql_error());
|
||||
error_log('Query was: ' . $query);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* function to insert new row in database
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param array $values Array with key=>values to update
|
||||
* @return boolean True on success, otherwise false.
|
||||
*
|
||||
*/
|
||||
public function save($table, $values)
|
||||
{
|
||||
$query= 'INSERT INTO ' . $table . " (";
|
||||
foreach ($values as $key=>$value){
|
||||
if ($value != null) $query = $query . $key . ",";
|
||||
}
|
||||
$query = rtrim($query, ",") . ') VALUES (';
|
||||
foreach ($values as $key=>$value){
|
||||
if ($value != null) $query = $query . "'" . $value . "',";
|
||||
}
|
||||
$query = rtrim($query, ",");
|
||||
$query = $query . ")";
|
||||
if (! mysql_query($query)){
|
||||
echo 'Query failed: ' . mysql_error();
|
||||
echo 'Query was: ' . $query;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* helper function to collect last row[s] in database
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param int $nr Number of rows to collect. NULL=>inifinity. DEFAULT=1.
|
||||
* @return mixed Array with values from Db row or 2d-array with multiple rows
|
||||
or false on failure.
|
||||
*
|
||||
*/
|
||||
public function last($table, $nr=1)
|
||||
{
|
||||
return Db::findBy($table, null, null, $nr, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* main function used to get rows from Db table.
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param string $key Column to select rows by
|
||||
* @param string $value Value to select rows by
|
||||
* @param int $nr Number of rows to collect. NULL=>inifinity. Default=NULL.
|
||||
* @param int $rev rev=1 indicates order should be reversed. Default=NULL.
|
||||
* @return mixed Array with values from Db row or 2d-array with multiple rows
|
||||
*
|
||||
*/
|
||||
public function findBy($table, $key, $value, $nr=null, $rev=null)
|
||||
{
|
||||
$query="SELECT * FROM " . $table;
|
||||
if ($key!=null) $query.= " WHERE " . $key . " = '" . $value . "'";
|
||||
if ($rev==1) $query.= " ORDER BY id DESC";
|
||||
if ($nr!=null) $query.= " LIMIT " . $nr;
|
||||
$result = mysql_query($query);
|
||||
if (! $result) {
|
||||
echo 'Query failed: ' . mysql_error();
|
||||
echo 'Query was: ' . $query;
|
||||
|
||||
return false;
|
||||
}
|
||||
if ($nr==1) {
|
||||
$row = mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
return $row;
|
||||
}
|
||||
else {
|
||||
$collection=array();
|
||||
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){
|
||||
$collection[]=$row;
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* main function used to get rows by multiple key=>value pairs from Db table.
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param array $where Array with column=>values to select rows by
|
||||
* @param int $nr Number of rows to collect. NULL=>inifinity. Default=NULL.
|
||||
* @param int $rev rev=1 indicates order should be reversed. Default=NULL.
|
||||
* @param string distinct Select rows with distinct columns, Default=NULL
|
||||
* @return mixed Array with values from Db row or 2d-array with multiple rows
|
||||
*
|
||||
*/
|
||||
public function findByMultiple($table, $where, $nr=null, $rev=null, $distinct=null)
|
||||
{
|
||||
$query="SELECT";
|
||||
if ($distinct!=null) {
|
||||
$query.= " DISTINCT " . $distinct;
|
||||
} else {
|
||||
$query.= " *";
|
||||
}
|
||||
$query.= " FROM " . $table;
|
||||
if ($where!=null){
|
||||
$query.= " WHERE";
|
||||
foreach ($where as $key=>$value) {
|
||||
$query.= " ". $key . " = '" . $value . "' and";
|
||||
}
|
||||
$query=rtrim($query, "and");
|
||||
$query=rtrim($query);
|
||||
}
|
||||
if ($rev==1) $query.= " ORDER BY id DESC";
|
||||
if ($nr!=null) $query.= " LIMIT " . $nr;
|
||||
$result = mysql_query($query);
|
||||
if (! $result) {
|
||||
echo 'Query failed: ' . mysql_error();
|
||||
echo 'Query was: ' . $query;
|
||||
|
||||
return false;
|
||||
}
|
||||
if ($nr==1) {
|
||||
$row = mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
return $row;
|
||||
}
|
||||
else {
|
||||
$collection=array();
|
||||
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){
|
||||
$collection[]=$row;
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* main function used to delete rows by multiple key=>value pairs from Db table.
|
||||
*
|
||||
* @param string $table Database table to delete row in
|
||||
* @param array $where Array with column=>values to select rows by
|
||||
* @param int $nr Number of rows to collect. NULL=>inifinity. Default=NULL.
|
||||
* @param int $rev rev=1 indicates order should be reversed. Default=NULL.
|
||||
* @param string distinct Select rows with distinct columns, Default=NULL
|
||||
* @return mixed Array with values from Db row or 2d-array with multiple rows
|
||||
*
|
||||
*/
|
||||
public function deleteByMultiple($table, $where, $nr=null, $rev=null)
|
||||
{
|
||||
$query="DELETE";
|
||||
$query.= " FROM " . $table;
|
||||
if ($where!=null){
|
||||
$query.= " WHERE";
|
||||
foreach ($where as $key=>$value) {
|
||||
$query.= " ". $key . " = '" . $value . "' and";
|
||||
}
|
||||
$query=rtrim($query, "and");
|
||||
$query=rtrim($query);
|
||||
}
|
||||
if ($rev==1) $query.= " ORDER BY id DESC";
|
||||
if ($nr!=null) $query.= " LIMIT " . $nr;
|
||||
$result = mysql_query($query);
|
||||
if (! $result) {
|
||||
echo 'Query failed: ' . mysql_error();
|
||||
echo 'Query was: ' . $query;
|
||||
|
||||
return false;
|
||||
}
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function customQuery($query)
|
||||
{
|
||||
return mysql_query($query);
|
||||
}
|
||||
/**
|
||||
* helper function used to get rows from Db table in reversed order.
|
||||
* defaults to obtaining 1 row.
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param string $key Column to select rows by
|
||||
* @param string $value Value to select rows by
|
||||
* @param int $nr Number of rows to collect. NULL=>inifinity. Default=1.
|
||||
* @return mixed Array with values from Db row or 2d-array with multiple rows or false on failure.
|
||||
*
|
||||
*/
|
||||
public function lastBy($table, $key, $value, $nr=1)
|
||||
{
|
||||
return Db::findBy($table, $key, $value, $nr, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function used to get rows from Db table in standard order.
|
||||
* defaults to obtaining 1 row.
|
||||
*
|
||||
* @param string $table Database table to update row in
|
||||
* @param string $key Column to select rows by
|
||||
* @param string $value Value to select rows by
|
||||
* @param int $nr Number of rows to collect. NULL=>inifinity. Default=1.
|
||||
* @return mixed Array with values from Db row or 2d-array with multiple rows or false on failure.
|
||||
*
|
||||
*/
|
||||
public function firstBy($table, $key, $value, $nr=1)
|
||||
{
|
||||
return Db::findBy($table, $key, $value, $nr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
42
tests/DbTest.php
Normal file
42
tests/DbTest.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../ykval-config.php');
|
||||
require_once(dirname(__FILE__) . '/../lib/Db.php');
|
||||
require_once 'PHPUnit/Framework.php';
|
||||
|
||||
|
||||
class DbTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function setup()
|
||||
{
|
||||
global $baseParams;
|
||||
$this->db=new Db($baseParams['__YKVAL_DB_HOST__'],
|
||||
'root',
|
||||
'lab',
|
||||
$baseParams['__YKVAL_DB_NAME__']);
|
||||
$this->db->connect();
|
||||
$this->db->customQuery("drop table unittest");
|
||||
$this->db->customQuery("create table unittest (value1 int, value2 int)");
|
||||
}
|
||||
public function test_template()
|
||||
{
|
||||
}
|
||||
|
||||
public function testConnect()
|
||||
{
|
||||
$this->assertTrue($this->db->isConnected());
|
||||
$this->db->disconnect();
|
||||
$this->assertFalse($this->db->isConnected());
|
||||
}
|
||||
public function testSave()
|
||||
{
|
||||
$this->assertTrue($this->db->save('unittest', array('value1'=>100,
|
||||
'value2'=>200)));
|
||||
$res=$this->db->findByMultiple('unittest', array('value1'=>100,
|
||||
'value2'=>200));
|
||||
$this->assertEquals(1, count($res));
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
186
tests/syncLibTest.php
Normal file
186
tests/syncLibTest.php
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
require_once 'PHPUnit/Framework.php';
|
||||
require_once (dirname(__FILE__) . '/../ykval-synclib.php');
|
||||
require_once(dirname(__FILE__) . '/../ykval-config.php');
|
||||
require_once(dirname(__FILE__) . '/../lib/Db.php');
|
||||
|
||||
class SyncLibTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function setup()
|
||||
{
|
||||
global $baseParams;
|
||||
$db = new Db($baseParams['__YKVAL_DB_HOST__'],
|
||||
'root',
|
||||
'lab',
|
||||
$baseParams['__YKVAL_DB_NAME__']);
|
||||
$db->connect();
|
||||
# $db->truncateTable('queue');
|
||||
$db->disconnect();
|
||||
}
|
||||
public function testTemplate()
|
||||
{
|
||||
}
|
||||
|
||||
public function testConstructor()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$this->assertGreaterThan(1, $sl->getNumberOfServers());
|
||||
$this->assertEquals($sl->getServer(0), "api2.yubico.com/wsapi/sync");
|
||||
}
|
||||
|
||||
|
||||
public function testQueue()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$nr_servers = $sl->getNumberOfServers();
|
||||
$queue_length = $sl->getQueueLength();
|
||||
|
||||
|
||||
$sl->queue(1259585588,
|
||||
"ccccccccccccfrhiutjgfnvgdurgliidceuilikvfhui",
|
||||
"cccccccccccc",
|
||||
10,
|
||||
20,
|
||||
100,
|
||||
1000);
|
||||
|
||||
$this->assertEquals($nr_servers + $queue_length, $sl->getQueueLength());
|
||||
$lastSync=$sl->getLast();
|
||||
$this->assertEquals($lastSync['modified'], 1259585588);
|
||||
$this->assertEquals($lastSync['otp'], "ccccccccccccfrhiutjgfnvgdurgliidceuilikvfhui");
|
||||
$this->assertEquals($lastSync['yk_identity'], "cccccccccccc");
|
||||
$this->assertEquals($lastSync['yk_counter'], 10);
|
||||
$this->assertEquals($lastSync['yk_use'], 20);
|
||||
$this->assertEquals($lastSync['yk_high'], 100);
|
||||
$this->assertEquals($lastSync['yk_low'], 1000);
|
||||
}
|
||||
|
||||
public function testCountersHigherThan()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$localParams=array('yk_counter'=>100,
|
||||
'yk_use'=>10);
|
||||
$otpParams=array('yk_counter'=>100,
|
||||
'yk_use'=>11);
|
||||
|
||||
$this->assertTrue($sl->countersHigherThan($otpParams, $localParams));
|
||||
$this->assertFalse($sl->countersHigherThan($localParams, $otpParams));
|
||||
$otpParams['yk_use']=10;
|
||||
$this->assertFalse($sl->countersHigherThan($otpParams, $localParams));
|
||||
$otpParams['yk_counter']=99;
|
||||
$this->assertFalse($sl->countersHigherThan($otpParams, $localParams));
|
||||
$otpParams['yk_counter']=101;
|
||||
$this->assertTrue($sl->countersHigherThan($otpParams, $localParams));
|
||||
}
|
||||
|
||||
public function testCountersHigherThanOrEqual()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$localParams=array('yk_counter'=>100,
|
||||
'yk_use'=>10);
|
||||
$otpParams=array('yk_counter'=>100,
|
||||
'yk_use'=>11);
|
||||
|
||||
$this->assertTrue($sl->countersHigherThanOrEqual($otpParams, $localParams));
|
||||
$this->assertFalse($sl->countersHigherThanOrEqual($localParams, $otpParams));
|
||||
$otpParams['yk_use']=10;
|
||||
$this->assertTrue($sl->countersHigherThanOrEqual($otpParams, $localParams));
|
||||
$otpParams['yk_counter']=99;
|
||||
$this->assertFalse($sl->countersHigherThanOrEqual($otpParams, $localParams));
|
||||
$otpParams['yk_counter']=101;
|
||||
$this->assertTrue($sl->countersHigherThanOrEqual($otpParams, $localParams));
|
||||
}
|
||||
|
||||
|
||||
public function testSync1()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$sl->syncServers = array("http://localhost/wsapi/syncvalid1",
|
||||
"http://localhost/wsapi/syncvalid2",
|
||||
"http://localhost/wsapi/syncvalid3");
|
||||
|
||||
$start_length=$sl->getQueueLength();
|
||||
$this->assertTrue($sl->queue(1259671571+1000,
|
||||
"ccccccccccccculnnjikvhjduicubtkcvgvkcdcvdjhk",
|
||||
"cccccccccccc",
|
||||
9,
|
||||
3,
|
||||
55,
|
||||
18000));
|
||||
|
||||
$res=$sl->sync(3);
|
||||
$this->assertEquals(3, $sl->getNumberOfValidAnswers());
|
||||
$this->assertTrue($res, "all sync servers should be configured to return ok values");
|
||||
$this->assertEquals($start_length, $sl->getQueueLength());
|
||||
|
||||
$this->assertTrue($sl->queue(1259671571+1000,
|
||||
"ccccccccccccculnnjikvhjduicubtkcvgvkcdcvdjhk",
|
||||
"cccccccccccc",
|
||||
9,
|
||||
3,
|
||||
55,
|
||||
18000));
|
||||
|
||||
$res=$sl->sync(2);
|
||||
$this->assertEquals(2, $sl->getNumberOfValidAnswers());
|
||||
$this->assertTrue($res, "all sync servers should be configured to return ok values");
|
||||
$this->assertEquals($start_length+1, $sl->getQueueLength());
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function testSync2()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$sl->syncServers = array("http://localhost/wsapi/syncinvalid1",
|
||||
"http://localhost/wsapi/syncinvalid2",
|
||||
"http://localhost/wsapi/syncinvalid3");
|
||||
|
||||
$start_length=$sl->getQueueLength();
|
||||
$this->assertTrue($sl->queue(1259671571+1000,
|
||||
"ccccccccccccculnnjikvhjduicubtkcvgvkcdcvdjhk",
|
||||
"cccccccccccc",
|
||||
9,
|
||||
3,
|
||||
55,
|
||||
18000));
|
||||
|
||||
$res=$sl->sync(3);
|
||||
$this->assertEquals(0, $sl->getNumberOfValidAnswers());
|
||||
$this->assertFalse($res, "only 1 sync server should have returned ok values");
|
||||
$this->assertEquals($start_length, $sl->getQueueLength());
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function testSync3()
|
||||
{
|
||||
$sl = new SyncLib();
|
||||
$sl->syncServers = array("http://localhost/wsapi/syncvalid1",
|
||||
"http://localhost/wsapi/syncvalid2",
|
||||
"http://localhost/wsapi/syncvalid3");
|
||||
|
||||
$start_length=$sl->getQueueLength();
|
||||
$this->assertTrue($sl->queue(1259671571+1000,
|
||||
"ccccccccccccculnnjikvhjduicubtkcvgvkcdcvdjhk",
|
||||
"cccccccccccc",
|
||||
9,
|
||||
3,
|
||||
55,
|
||||
18000));
|
||||
|
||||
$res=$sl->sync(1);
|
||||
$this->assertEquals(1, $sl->getNumberOfValidAnswers());
|
||||
$this->assertTrue($res, "only 1 sync server should have returned ok values");
|
||||
$this->assertEquals($start_length+2, $sl->getQueueLength());
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function testActivateQueue()
|
||||
{
|
||||
}
|
||||
}
|
||||
?>
|
@ -9,6 +9,8 @@ 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('TS_SEC', 1/8);
|
||||
define('TS_REL_TOLERANCE', 0.3);
|
||||
@ -74,6 +76,19 @@ function getUTCTimeStamp() {
|
||||
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) {
|
||||
@ -113,9 +128,9 @@ function modhex2b64 ($modhex_str) {
|
||||
// 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 ^OK is returned,
|
||||
// or if all URLs failed, false.
|
||||
function retrieveURLasync ($urls) {
|
||||
// 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 ($urls, $ans_req=1, $match="^OK", $returl=False) {
|
||||
$mh = curl_multi_init();
|
||||
|
||||
$ch = array();
|
||||
@ -134,6 +149,8 @@ function retrieveURLasync ($urls) {
|
||||
}
|
||||
|
||||
$str = false;
|
||||
$ans_count = 0;
|
||||
$ans_arr = array();
|
||||
|
||||
do {
|
||||
while (($mrc = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM)
|
||||
@ -143,22 +160,29 @@ function retrieveURLasync ($urls) {
|
||||
debug ("YK-KSM multi", $info);
|
||||
if ($info['result'] == CURL_OK) {
|
||||
$str = curl_multi_getcontent($info['handle']);
|
||||
|
||||
if (preg_match("/^OK/", $str)) {
|
||||
debug($str);
|
||||
if (preg_match("/".$match."/", $str)) {
|
||||
$error = curl_error ($info['handle']);
|
||||
$errno = curl_errno ($info['handle']);
|
||||
$info = curl_getinfo ($info['handle']);
|
||||
debug("YK-KSM errno/error: " . $errno . "/" . $error, $info);
|
||||
$cinfo = curl_getinfo ($info['handle']);
|
||||
debug("YK-KSM errno/error: " . $errno . "/" . $error, $cinfo);
|
||||
$ans_count++;
|
||||
debug("found entry");
|
||||
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 $str;
|
||||
|
||||
if ($ans_count==1) return $ans_arr[0];
|
||||
else return $ans_arr;
|
||||
}
|
||||
|
||||
|
||||
curl_multi_remove_handle ($mh, $info['handle']);
|
||||
curl_close ($info['handle']);
|
||||
unset ($ch[$info['handle']]);
|
||||
|
@ -5,7 +5,9 @@ $baseParams = array ();
|
||||
$baseParams['__YKVAL_DB_HOST__'] = 'localhost';
|
||||
$baseParams['__YKVAL_DB_NAME__'] = 'ykval';
|
||||
$baseParams['__YKVAL_DB_USER__'] = 'ykval_verifier';
|
||||
$baseParams['__YKVAL_DB_PW__'] = 'password';
|
||||
$baseParams['__YKVAL_DB_PW__'] = 'lab';
|
||||
# For the validation server sync
|
||||
$baseParams['__YKVAL_SYNC_POOL__'] = "api2.yubico.com/wsapi/sync;api3.yubico.com/wsapi/sync;api4.yubico.com/wsapi/sync";
|
||||
|
||||
# For the get-api-key service.
|
||||
$baseParams['__YKGAK_DB_HOST__'] = $baseParams['__YKVAL_DB_HOST__'];
|
||||
@ -22,6 +24,9 @@ $baseParams['__YKR_DB_USER__'] = 'ykval_revoke';
|
||||
$baseParams['__YKR_DB_PW__'] = 'thirdpassword';
|
||||
$baseParams['__YKR_IP__'] = '1.2.3.4';
|
||||
|
||||
|
||||
|
||||
|
||||
// otp2ksmurls: Return array of YK-KSM URLs for decrypting OTP for
|
||||
// CLIENT. The URLs must be fully qualified, i.e., contain the OTP
|
||||
// itself.
|
||||
|
@ -30,8 +30,8 @@ CREATE TABLE yubikeys (
|
||||
|
||||
CREATE TABLE queue (
|
||||
id INT NOT NULL UNIQUE AUTO_INCREMENT,
|
||||
queued_time DATETIME DEFAULT NOW(),
|
||||
modified_time DATETIME DEFAULT NOW(),
|
||||
queued_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
modified_time TIMESTAMP,
|
||||
otp VARCHAR(100) NOT NULL,
|
||||
server VARCHAR(100) NOT NULL,
|
||||
info VARCHAR(100) NOT NULL,
|
||||
@ -44,6 +44,8 @@ GRANT SELECT,INSERT,UPDATE(accessed, counter, low, high, sessionUse)
|
||||
ON ykval.yubikeys to 'ykval_verifier'@'localhost';
|
||||
GRANT SELECT(id, secret, active)
|
||||
ON ykval.clients to 'ykval_verifier'@'localhost';
|
||||
GRANT SELECT,INSERT,UPDATE,DELETE
|
||||
ON ykval.queue to 'ykval_verifier'@'localhost';
|
||||
|
||||
-- DROP USER 'ykval_getapikey'@'localhost';
|
||||
CREATE USER 'ykval_getapikey'@'localhost';
|
||||
|
144
ykval-sync.php
Normal file
144
ykval-sync.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'ykval-config.php';
|
||||
|
||||
$apiKey = '';
|
||||
|
||||
header("content-type: text/plain");
|
||||
|
||||
debug("Request: " . $_SERVER['QUERY_STRING']);
|
||||
|
||||
$conn = mysql_connect($baseParams['__YKVAL_DB_HOST__'],
|
||||
$baseParams['__YKVAL_DB_USER__'],
|
||||
$baseParams['__YKVAL_DB_PW__']);
|
||||
if (!$conn) {
|
||||
sendResp(S_BACKEND_ERROR, $apiKey);
|
||||
exit;
|
||||
}
|
||||
if (!mysql_select_db($baseParams['__YKVAL_DB_NAME__'], $conn)) {
|
||||
sendResp(S_BACKEND_ERROR, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
#
|
||||
# Define requirements on protocoll
|
||||
#
|
||||
|
||||
$syncParams=array("modified"=>Null,
|
||||
"otp"=>Null,
|
||||
"yk_identity"=>Null,
|
||||
"yk_counter"=>Null,
|
||||
"yk_use"=>Null,
|
||||
"yk_high"=>Null,
|
||||
"yk_low"=>Null);
|
||||
|
||||
#
|
||||
# Extract values from HTTP request
|
||||
#
|
||||
|
||||
$tmp_log = "ykval-sync received ";
|
||||
foreach ($syncParams as $param=>$value) {
|
||||
$value = getHttpVal($param, Null);
|
||||
if ($value==Null) {
|
||||
debug("ykval-sync recevied request with parameter[s] missing");
|
||||
sendResp(S_MISSING_PARAMETER, '');
|
||||
exit;
|
||||
}
|
||||
$syncParams[$param]=$value;
|
||||
$local_log .= "$param=$value ";
|
||||
}
|
||||
debug($tmp_log);
|
||||
|
||||
#
|
||||
# Get local counter data
|
||||
#
|
||||
|
||||
$devId = $syncParams['yk_identity'];
|
||||
$ad = getAuthData($conn, $devId);
|
||||
if (!is_array($ad)) {
|
||||
debug('Discovered Yubikey ' . $devId);
|
||||
addNewKey($conn, $devId);
|
||||
$ad = getAuthData($conn, $devId);
|
||||
if (!is_array($ad)) {
|
||||
debug('Invalid Yubikey ' . $devId);
|
||||
sendResp(S_BACKEND_ERROR, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
debug("Auth data:", $ad);
|
||||
if ($ad['active'] != 1) {
|
||||
debug('De-activated Yubikey ' . $devId);
|
||||
sendResp(S_BAD_OTP, $apiKey);
|
||||
exit;
|
||||
}
|
||||
|
||||
# Note: AD comes directly from the DB response. Since we want to separate
|
||||
# DB-dependencies longterm, we parse out the values we want from the response
|
||||
# in order to keep naming consistent in the remaining code. This could be
|
||||
# considered inefficent in terms of computing power.
|
||||
$localParams=array('modified'=>DbTimeToUnix($ad['accessed']),
|
||||
'yk_counter'=>$ad['counter'],
|
||||
'yk_use'=>$ad['sessionUse'],
|
||||
'yk_low'=>$ad['low'],
|
||||
'yk_high'=>$ad['high']);
|
||||
|
||||
#
|
||||
# Compare sync and local counters and generate warnings according to
|
||||
#
|
||||
# http://code.google.com/p/yubikey-val-server-php/wiki/ServerReplicationProtocol
|
||||
#
|
||||
|
||||
if ($syncParams['yk_counter'] > $localParams['yk_counter'] ||
|
||||
($syncParams['yk_counter'] == $localParams['yk_counter'] &&
|
||||
$syncParams['yk_use'] > $localParams['yk_use'])) {
|
||||
# sync counters are higher than local counters. We should update database
|
||||
|
||||
#TODO: Take care of accessed field. What format should be used. seconds since epoch?
|
||||
$stmt = 'UPDATE yubikeys SET ' .
|
||||
'accessed=\'' . UnixToDbTime($syncParams['modified']) . '\'' .
|
||||
', counter=' . $syncParams['yk_counter'] .
|
||||
', sessionUse=' . $syncParams['yk_use'] .
|
||||
', low=' . $syncParams['yk_low'] .
|
||||
', high=' . $syncParams['yk_high'] .
|
||||
' WHERE id=' . $ad['id'];
|
||||
query($conn, $stmt);
|
||||
|
||||
} else {
|
||||
if ($syncParams['yk_counter']==$localParams['yk_counter'] &&
|
||||
$syncParams['yk_use']==$localParams['yk_use']) {
|
||||
# sync counters are equal to local counters.
|
||||
if ($syncParams['modified']==$localParams['modified']) {
|
||||
# sync modified is equal to local modified. Sync request is unnessecarily sent, we log a "light" warning
|
||||
error_log("ykval-sync:notice:Sync request unnessecarily sent");
|
||||
} else {
|
||||
# sync modified is not equal to local modified. We have an OTP replay attempt somewhere in the system
|
||||
error_log("ykval-sync:warning:Replayed OTP attempt. " .
|
||||
" identity=" . $syncParams['yk_identity'] .
|
||||
" otp=" . $syncParams['otp'] .
|
||||
" syncCounter=" . $syncParams['yk_counter'] .
|
||||
" syncUse=" . $syncParams['yk_use'] .
|
||||
" syncModified=" . $syncParams['modified'] .
|
||||
" localModified=" . $localParams['modified']);
|
||||
}
|
||||
} else {
|
||||
# sync counters are lower than local counters
|
||||
error_log("ykval-sync:warning:Remote server is out of sync." .
|
||||
" identity=" . $syncParams['yk_identity'] .
|
||||
" syncCounter=" . $syncParams['yk_counter'] .
|
||||
" syncUse=" . $syncParams['yk_use'].
|
||||
" localCounter=" . $localParams['yk_counter'] .
|
||||
" localUse=" . $localParams['yk_use']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$extra=array('modified'=>$localParams['modified'],
|
||||
'yk_identity'=>$syncParams['yk_identity'], #NOTE: Identity is never picked out from local db
|
||||
'yk_counter'=>$localParams['yk_counter'],
|
||||
'yk_use'=>$localParams['yk_use'],
|
||||
'yk_high'=>$localParams['yk_high'],
|
||||
'yk_low'=>$localParams['yk_low']);
|
||||
|
||||
sendResp(S_OK, '', $extra);
|
||||
|
||||
?>
|
346
ykval-synclib.php
Normal file
346
ykval-synclib.php
Normal file
@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
require_once 'ykval-config.php';
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'lib/Db.php';
|
||||
|
||||
class SyncLib
|
||||
{
|
||||
public $syncServers = null;
|
||||
public $dbConn = null;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
global $baseParams;
|
||||
$this->syncServers = explode(";", $baseParams['__YKVAL_SYNC_POOL__']);
|
||||
$this->db=new Db($baseParams['__YKVAL_DB_HOST__'],
|
||||
$baseParams['__YKVAL_DB_USER__'],
|
||||
$baseParams['__YKVAL_DB_PW__'],
|
||||
$baseParams['__YKVAL_DB_NAME__']);
|
||||
$this->db->connect();
|
||||
$this->random_key=rand(0,1<<16);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
function getServer($index)
|
||||
{
|
||||
if (isset($this->syncServers[$index])) return $this->syncServers[$index];
|
||||
else return "";
|
||||
}
|
||||
function getLast()
|
||||
{
|
||||
$res=$this->db->last('queue', 1);
|
||||
parse_str($res['info'], $info);
|
||||
return array('modified'=>$this->DbTimeToUnix($res['modified_time']),
|
||||
'otp'=>$res['otp'],
|
||||
'server'=>$res['server'],
|
||||
'yk_identity'=>$info['yk_identity'],
|
||||
'yk_counter'=>$info['yk_counter'],
|
||||
'yk_use'=>$info['yk_use'],
|
||||
'yk_high'=>$info['yk_high'],
|
||||
'yk_low'=>$info['yk_low']);
|
||||
}
|
||||
public function getQueueLength()
|
||||
{
|
||||
return count($this->db->last('queue', NULL));
|
||||
}
|
||||
public function queue($modified, $otp, $identity, $counter, $use, $high, $low)
|
||||
{
|
||||
$info='yk_identity=' . $identity .
|
||||
'&yk_counter=' . $counter .
|
||||
'&yk_use=' . $use .
|
||||
'&yk_high=' . $high .
|
||||
'&yk_low=' . $low;
|
||||
|
||||
$this->otpParams['modified']=$modified;
|
||||
$this->otpParams['otp']=$otp;
|
||||
$this->otpParams['yk_identity']=$identity;
|
||||
$this->otpParams['yk_counter']=$counter;
|
||||
$this->otpParams['yk_use']=$use;
|
||||
$this->otpParams['yk_high']=$high;
|
||||
$this->otpParams['yk_low']=$low;
|
||||
|
||||
$res=True;
|
||||
foreach ($this->syncServers as $server) {
|
||||
|
||||
if(! $this->db->save('queue', array('modified_time'=>$this->UnixToDbTime($modified),
|
||||
'otp'=>$otp,
|
||||
'server'=>$server,
|
||||
'random_key'=>$this->random_key,
|
||||
'info'=>$info))) $res=False;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
public function getNumberOfServers()
|
||||
{
|
||||
if (is_array($this->syncServers)) return count($this->syncServers);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
private function log($level, $msg, $params=NULL)
|
||||
{
|
||||
$logMsg="ykval-synclib:" . $level . ":" . $msg;
|
||||
if ($params) $logMsg .= " modified=" . $params['modified'] .
|
||||
" yk_identity=" . $params['yk_identity'] .
|
||||
" yk_counter=" . $params['yk_counter'] .
|
||||
" yk_use=" . $params['yk_use'] .
|
||||
" yk_high=" . $params['yk_high'] .
|
||||
" yk_low=" . $params['yk_low'];
|
||||
error_log($logMsg);
|
||||
}
|
||||
private function getLocalParams($yk_identity)
|
||||
{
|
||||
$this->log("notice", "searching for " . $yk_identity . " (" . modhex2b64($yk_identity) . ") in local db");
|
||||
$res = $this->db->lastBy('yubikeys', 'publicName', modhex2b64($yk_identity));
|
||||
$localParams=array('modified'=>$this->DbTimeToUnix($res['accessed']),
|
||||
'yk_identity'=>$yk_identity,
|
||||
'yk_counter'=>$res['counter'],
|
||||
'yk_use'=>$res['sessionUse'],
|
||||
'yk_high'=>$res['high'],
|
||||
'yk_low'=>$res['low']);
|
||||
|
||||
$this->log("notice", "counter found in db ", $localParams);
|
||||
|
||||
return $localParams;
|
||||
|
||||
}
|
||||
|
||||
private function parseParamsFromMultiLineString($str)
|
||||
{
|
||||
preg_match("/^modified=([0-9]*)/m", $str, $out);
|
||||
$resParams['modified']=$out[1];
|
||||
preg_match("/^yk_identity=([[:alpha:]]*)/m", $str, $out);
|
||||
$resParams['yk_identity']=$out[1];
|
||||
preg_match("/^yk_counter=([0-9]*)/m", $str, $out);
|
||||
$resParams['yk_counter']=$out[1];
|
||||
preg_match("/^yk_use=([0-9]*)/m", $str, $out);
|
||||
$resParams['yk_use']=$out[1];
|
||||
preg_match("/^yk_high=([0-9]*)/m", $str, $out);
|
||||
$resParams['yk_high']=$out[1];
|
||||
preg_match("/^yk_low=([0-9]*)/m", $str, $out);
|
||||
$resParams['yk_low']=$out[1];
|
||||
|
||||
return $resParams;
|
||||
}
|
||||
|
||||
public function updateDbCounters($params)
|
||||
{
|
||||
|
||||
|
||||
$res=$this->db->lastBy('yubikeys', 'publicName', modhex2b64($params['yk_identity']));
|
||||
if (isset($res['id'])) {
|
||||
if(! $this->db->update('yubikeys', $res['id'], array('accessed'=>$this->UnixToDbTime($params['modified']),
|
||||
'counter'=>$params['yk_counter'],
|
||||
'sessionUse'=>$params['yk_use'],
|
||||
'low'=>$params['yk_low'],
|
||||
'high'=>$params['yk_high'])))
|
||||
{
|
||||
error_log("ykval-synclib:critical: failed to update internal DB with new counters");
|
||||
return false;
|
||||
} else {
|
||||
$this->log("notice", "updated database ", $params);
|
||||
return true;
|
||||
}
|
||||
} else return false;
|
||||
}
|
||||
|
||||
public function countersHigherThan($p1, $p2)
|
||||
{
|
||||
if ($p1['yk_counter'] > $p2['yk_counter'] ||
|
||||
($p1['yk_counter'] == $p2['yk_counter'] &&
|
||||
$p1['yk_use'] > $p2['yk_use'])) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function countersHigherThanOrEqual($p1, $p2)
|
||||
{
|
||||
if ($p1['yk_counter'] > $p2['yk_counter'] ||
|
||||
($p1['yk_counter'] == $p2['yk_counter'] &&
|
||||
$p1['yk_use'] >= $p2['yk_use'])) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function sync($ans_req)
|
||||
{
|
||||
#
|
||||
# Construct URLs
|
||||
#
|
||||
|
||||
$urls=array();
|
||||
$res=$this->db->findByMultiple('queue', array("modified_time"=>$this->UnixToDbTime($this->otpParams['modified']), "random_key"=>$this->random_key));
|
||||
foreach ($res as $row) {
|
||||
$urls[]=$row['server'] . '?' . $row['info'];
|
||||
}
|
||||
|
||||
#
|
||||
# Send out requests
|
||||
#
|
||||
if (count($urls)>=$ans_req) $ans_arr=$this->retrieveURLasync($urls, $ans_req);
|
||||
else return false;
|
||||
|
||||
if (!is_array($ans_arr)) {
|
||||
$this->log('warning', 'No responses from validation server pool');
|
||||
$ans_arr=array();
|
||||
}
|
||||
|
||||
#
|
||||
# Parse responses
|
||||
#
|
||||
$localParams=$this->getLocalParams($this->otpParams['yk_identity']);
|
||||
|
||||
$this->answers = count($ans_arr);
|
||||
$this->valid_answers = 0;
|
||||
foreach ($ans_arr as $answer){
|
||||
// Parse out parameters from each response
|
||||
$resParams=$this->parseParamsFromMultiLineString($answer);
|
||||
$this->log("notice", "local db contains ", $localParams);
|
||||
$this->log("notice", "response contains ", $resParams);
|
||||
|
||||
# Check if internal DB should be updated
|
||||
if ($this->countersHigherThan($resParams, $localParams)) {
|
||||
$this->updateDbCounters($resParams);
|
||||
}
|
||||
|
||||
# Check for warnings
|
||||
#
|
||||
# If received sync response have lower counters than locally saved last counters
|
||||
# (indicating that remote server wasn't synced)
|
||||
|
||||
if ($this->countersHigherThan($localParams, $resParams)) {
|
||||
$this->log("warning", "Remote server out of sync, local counters ", $localParams);
|
||||
$this->log("warning", "Remote server out of sync, remote counters ", $resParams);
|
||||
}
|
||||
|
||||
# If received sync response have higher counters than locally saved last counters
|
||||
# (indicating that local server wasn't synced)
|
||||
if ($this->countersHigherThan($resParams, $localParams)) {
|
||||
$this->log("warning", "Local server out of sync, local counters ", $localParams);
|
||||
$this->log("warning", "Local server out of sync, remote counters ", $resParams);
|
||||
}
|
||||
|
||||
# If received sync response have higher counters than OTP counters
|
||||
# (indicating REPLAYED_OTP)
|
||||
if ($this->countersHigherThanOrEqual($resParams, $this->otpParams)) {
|
||||
$this->log("warning", "replayed OTP, remote counters " , $resParams);
|
||||
$this->log("warning", "replayed OTP, otp counters", $this->otpParams);
|
||||
}
|
||||
|
||||
|
||||
# Check if answer marks OTP as valid
|
||||
if (!$this->countersHigherThanOrEqual($resParams, $this->otpParams)) $this->valid_answers++;
|
||||
|
||||
# Delete entry from table
|
||||
preg_match('/url=(.*)\?/', $answer, $out);
|
||||
$server=$out[1];
|
||||
debug("server=" . $server);
|
||||
$this->db->deleteByMultiple('queue', array("modified_time"=>$this->UnixToDbTime($this->otpParams['modified']), "random_key"=>$this->random_key, 'server'=>$server));
|
||||
|
||||
}
|
||||
|
||||
/* Return true if valid answers equals required answers. Since we only obtain the required
|
||||
amount of answers from retrieveAsync this indicates that all answers were actually valid.
|
||||
Otherwise, return false. */
|
||||
if ($this->valid_answers==$ans_req) return True;
|
||||
else return False;
|
||||
}
|
||||
public function getNumberOfValidAnswers()
|
||||
{
|
||||
if (isset($this->valid_answers)) return $this->valid_answers;
|
||||
else return 0;
|
||||
}
|
||||
public function getNumberOfAnswers()
|
||||
{
|
||||
if (isset($this->answers)) return $this->answers;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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 ($urls, $ans_req=1) {
|
||||
$mh = curl_multi_init();
|
||||
|
||||
$ch = array();
|
||||
foreach ($urls as $id => $url) {
|
||||
$handle = curl_init();
|
||||
|
||||
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, 10);
|
||||
|
||||
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)) {
|
||||
debug ("YK-KSM multi", $info);
|
||||
if ($info['result'] == CURL_OK) {
|
||||
$str = curl_multi_getcontent($info['handle']);
|
||||
debug($str);
|
||||
if (preg_match("/status=OK/", $str)) {
|
||||
$error = curl_error ($info['handle']);
|
||||
$errno = curl_errno ($info['handle']);
|
||||
$cinfo = curl_getinfo ($info['handle']);
|
||||
debug("YK-KSM errno/error: " . $errno . "/" . $error, $cinfo);
|
||||
$ans_count++;
|
||||
debug("found entry");
|
||||
$ans_arr[]="url=" . $cinfo['url'] . "\n" . $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);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
require_once 'ykval-common.php';
|
||||
require_once 'ykval-config.php';
|
||||
require_once 'ykval-synclib.php';
|
||||
|
||||
$apiKey = '';
|
||||
|
||||
@ -146,7 +147,52 @@ $stmt = 'UPDATE yubikeys SET accessed=NOW()' .
|
||||
', low=' . $otpinfo['low'] .
|
||||
', high=' . $otpinfo['high'] .
|
||||
' WHERE id=' . $ad['id'];
|
||||
$r=query($conn, $stmt);
|
||||
|
||||
$stmt = 'SELECT accessed FROM yubikeys WHERE id=' . $ad['id'];
|
||||
$r=query($conn, $stmt);
|
||||
if (mysql_num_rows($r) > 0) {
|
||||
$row = mysql_fetch_assoc($r);
|
||||
mysql_free_result($r);
|
||||
$modified=DbTimeToUnix($row['accessed']);
|
||||
}
|
||||
else {
|
||||
$modified=0;
|
||||
}
|
||||
|
||||
//// Queue sync requests
|
||||
$sl = new SyncLib();
|
||||
// We need the modifed value from the DB
|
||||
$stmp = 'SELECT accessed FROM yubikeys WHERE id=' . $ad['id'];
|
||||
query($conn, $stmt);
|
||||
$sl->queue($modified,
|
||||
$otp,
|
||||
$devId,
|
||||
$otpinfo['session_counter'],
|
||||
$otpinfo['session_use'],
|
||||
$otpinfo['high'],
|
||||
$otpinfo['low']);
|
||||
$required_answers=$sl->getNumberOfServers();
|
||||
$syncres=$sl->sync($required_answers);
|
||||
$answers=$sl->getNumberOfAnswers();
|
||||
$valid_answers=$sl->getNumberOfValidAnswers();
|
||||
|
||||
debug("ykval-verify:notice:number of servers=" . $required_answers);
|
||||
debug("ykval-verify:notice:number of answers=" . $answers);
|
||||
debug("ykval-verify:notice:number of valid answers=" . $valid_answers);
|
||||
if($syncres==False) {
|
||||
# sync returned false, indicating that
|
||||
# either at least 1 answer marked OTP as invalid or
|
||||
# there were not enough answers
|
||||
debug("ykval-verify:notice:Sync failed");
|
||||
if ($valid_answers!=$answers) {
|
||||
sendResp(S_REPLAYED_OTP, $apiKey);
|
||||
exit;
|
||||
} else {
|
||||
sendResp(S_NOT_ENOUGH_ANSWERS, $apiKey);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
//// Check the time stamp
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user