1
0
mirror of https://github.com/Yubico/yubikey-val.git synced 2025-04-10 05:02:26 +02:00

Merge branch 'master' of github.com:Yubico/yubikey-val-server-php

Conflicts:
	ykval-synclib.php
This commit is contained in:
Klas Lindfors 2012-06-11 12:54:55 +02:00
commit 060b35453d
16 changed files with 237 additions and 235 deletions

View File

@ -29,7 +29,7 @@ $myLog = new Log($logname);
$db=new Db($baseParams['__YKVAL_DB_DSN__'], $db=new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
$logname . ':db'); $logname . ':db');
if (!$db->connect()) { if (!$db->connect()) {

View File

@ -64,7 +64,7 @@ function debug() {
} }
// Return eg. 2008-11-21T06:11:55Z0711 // Return eg. 2008-11-21T06:11:55Z0711
// //
function getUTCTimeStamp() { function getUTCTimeStamp() {
date_default_timezone_set('UTC'); date_default_timezone_set('UTC');
$tiny = substr(microtime(false), 2, 3); $tiny = substr(microtime(false), 2, 3);
@ -72,7 +72,7 @@ function getUTCTimeStamp() {
} }
# NOTE: When we evolve to using general DB-interface, this functinality # NOTE: When we evolve to using general DB-interface, this functinality
# should be moved there. # should be moved there.
function DbTimeToUnix($db_time) function DbTimeToUnix($db_time)
{ {
$unix=strptime($db_time, '%F %H:%M:%S'); $unix=strptime($db_time, '%F %H:%M:%S');
@ -82,14 +82,14 @@ function DbTimeToUnix($db_time)
function UnixToDbTime($unix) function UnixToDbTime($unix)
{ {
return date('Y-m-d H:i:s', $unix); return date('Y-m-d H:i:s', $unix);
} }
// Sign a http query string in the array of key-value pairs // Sign a http query string in the array of key-value pairs
// return b64 encoded hmac hash // return b64 encoded hmac hash
function sign($a, $apiKey) { function sign($a, $apiKey) {
ksort($a); ksort($a);
$qs = urldecode(http_build_query($a)); $qs = urldecode(http_build_query($a));
// the TRUE at the end states we want the raw value, not hexadecimal form // the TRUE at the end states we want the raw value, not hexadecimal form
$hmac = hash_hmac('sha1', utf8_encode($qs), $apiKey, true); $hmac = hash_hmac('sha1', utf8_encode($qs), $apiKey, true);
$hmac = base64_encode($hmac); $hmac = base64_encode($hmac);
@ -97,7 +97,7 @@ function sign($a, $apiKey) {
debug('SIGN: ' . $qs . ' H=' . $hmac); debug('SIGN: ' . $qs . ' H=' . $hmac);
return $hmac; return $hmac;
} // sign an array of query string } // sign an array of query string
function hex2b64 ($hex_str) { function hex2b64 ($hex_str) {
@ -115,7 +115,7 @@ function modhex2b64 ($modhex_str) {
// The request are sent asynchronously. Some of the URLs can fail // The request are sent asynchronously. Some of the URLs can fail
// with unknown host, connection errors, or network timeout, but as // with unknown host, connection errors, or network timeout, but as
// long as one of the URLs given work, data will be returned. If all // 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 // URLs fail, data from some URL that did not match parameter $match
// (defaults to ^OK) is returned, or if all URLs failed, false. // (defaults to ^OK) is returned, or if all URLs failed, false.
function retrieveURLasync ($urls, $ans_req=1, $match="^OK", $returl=False) { function retrieveURLasync ($urls, $ans_req=1, $match="^OK", $returl=False) {
$mh = curl_multi_init(); $mh = curl_multi_init();
@ -165,11 +165,11 @@ function retrieveURLasync ($urls, $ans_req=1, $match="^OK", $returl=False) {
curl_close ($h); curl_close ($h);
} }
curl_multi_close ($mh); curl_multi_close ($mh);
if ($ans_count==1) return $ans_arr[0]; if ($ans_count==1) return $ans_arr[0];
else return $ans_arr; else return $ans_arr;
} }
curl_multi_remove_handle ($mh, $info['handle']); curl_multi_remove_handle ($mh, $info['handle']);
curl_close ($info['handle']); curl_close ($info['handle']);
unset ($ch[$info['handle']]); unset ($ch[$info['handle']]);

View File

@ -8,13 +8,13 @@ $baseParams['__YKVAL_DB_PW__'] = 'lab';
$baseParams['__YKVAL_DB_OPTIONS__'] = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); $baseParams['__YKVAL_DB_OPTIONS__'] = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
# For the validation server sync # For the validation server sync
$baseParams['__YKVAL_SYNC_POOL__'] = array("http://api2.example.com/wsapi/2.0/sync", $baseParams['__YKVAL_SYNC_POOL__'] = array("http://api2.example.com/wsapi/2.0/sync",
"http://api3.example.com/wsapi/2.0/sync", "http://api3.example.com/wsapi/2.0/sync",
"http://api4.example.com/wsapi/2.0/sync"); "http://api4.example.com/wsapi/2.0/sync");
# An array of IP addresses allowed to issue sync requests # An array of IP addresses allowed to issue sync requests
# NOTE: You must use IP addresses here. # NOTE: You must use IP addresses here.
$baseParams['__YKVAL_ALLOWED_SYNC_POOL__'] = array("1.2.3.4", $baseParams['__YKVAL_ALLOWED_SYNC_POOL__'] = array("1.2.3.4",
"2.3.4.5", "2.3.4.5",
"3.4.5.6"); "3.4.5.6");
# Specify how often the sync daemon awakens # Specify how often the sync daemon awakens

View File

@ -18,7 +18,7 @@ class Db
* @param string $user Database user * @param string $user Database user
* @param string $pwd Database password * @param string $pwd Database password
* @param string $name Database table name * @param string $name Database table name
* @return void * @return void
* *
*/ */
public function __construct($db_dsn, $db_username, $db_password, $db_options, $name='ykval-db') public function __construct($db_dsn, $db_username, $db_password, $db_options, $name='ykval-db')
@ -39,7 +39,7 @@ class Db
/** /**
* function to convert Db timestamps to unixtime(s) * function to convert Db timestamps to unixtime(s)
* *
* @param string $updated Database timestamp * @param string $updated Database timestamp
* @return int Timestamp in unixtime format * @return int Timestamp in unixtime format
* *
*/ */
@ -53,7 +53,7 @@ class Db
/** /**
* function to compute delta (s) between 2 Db timestamps * function to compute delta (s) between 2 Db timestamps
* *
* @param string $first Database timestamp 1 * @param string $first Database timestamp 1
* @param string $second Database timestamp 2 * @param string $second Database timestamp 2
* @return int Deltatime (s) * @return int Deltatime (s)
* *
@ -73,7 +73,7 @@ class Db
{ {
$this->dbh=NULL; $this->dbh=NULL;
} }
/** /**
* function to check if database is connected * function to check if database is connected
* *
@ -109,7 +109,7 @@ class Db
} }
if($this->isConnected()) { if($this->isConnected()) {
$this->myLog->log(LOG_DEBUG, 'DB query is: ' . $query); $this->myLog->log(LOG_DEBUG, 'DB query is: ' . $query);
try { try {
$this->result = $this->dbh->query($query); $this->result = $this->dbh->query($query);
} catch (PDOException $e) { } catch (PDOException $e) {
@ -154,8 +154,8 @@ class Db
$query = rtrim($query, ",") . " WHERE " . $k . " = '" . $v . "'"; $query = rtrim($query, ",") . " WHERE " . $k . " = '" . $v . "'";
// Insert UPDATE statement at beginning // Insert UPDATE statement at beginning
$query = "UPDATE " . $table . " SET " . $query; $query = "UPDATE " . $table . " SET " . $query;
return $this->query($query, false); return $this->query($query, false);
} }
@ -199,11 +199,11 @@ class Db
$query = rtrim($query, ",") . " WHERE " . $k . " = '" . $v . "' and " . $condition; $query = rtrim($query, ",") . " WHERE " . $k . " = '" . $v . "' and " . $condition;
// Insert UPDATE statement at beginning // Insert UPDATE statement at beginning
$query = "UPDATE " . $table . " SET " . $query; $query = "UPDATE " . $table . " SET " . $query;
return $this->query($query, false); return $this->query($query, false);
} }
/** /**
* Function to update row in database based on a condition. * Function to update row in database based on a condition.
@ -258,7 +258,7 @@ or false on failure.
} }
/** /**
* main function used to get rows from Db table. * main function used to get rows from Db table.
* *
* @param string $table Database table to update row in * @param string $table Database table to update row in
* @param string $key Column to select rows by * @param string $key Column to select rows by
@ -274,7 +274,7 @@ or false on failure.
} }
/** /**
* main function used to get rows by multiple key=>value pairs from Db table. * main function used to get rows by multiple key=>value pairs from Db table.
* *
* @param string $table Database table to update row in * @param string $table Database table to update row in
* @param array $where Array with column=>values to select rows by * @param array $where Array with column=>values to select rows by
@ -295,7 +295,7 @@ or false on failure.
$query.= " *"; $query.= " *";
} }
$query.= " FROM " . $table; $query.= " FROM " . $table;
if ($where!=null){ if ($where!=null){
foreach ($where as $key=>$value) { foreach ($where as $key=>$value) {
if ($key!=null) { if ($key!=null) {
if ($value!=null) $match.= " ". $key . " = '" . $value . "' and"; if ($value!=null) $match.= " ". $key . " = '" . $value . "' and";
@ -311,12 +311,12 @@ or false on failure.
$result = $this->query($query, true); $result = $this->query($query, true);
if (!$result) return false; if (!$result) return false;
if ($nr==1) { if ($nr==1) {
$row = $result->fetch(PDO::FETCH_ASSOC); $row = $result->fetch(PDO::FETCH_ASSOC);
$result->closeCursor(); $result->closeCursor();
return $row; return $row;
} }
else { else {
$collection=array(); $collection=array();
while($row = $result->fetch(PDO::FETCH_ASSOC)){ while($row = $result->fetch(PDO::FETCH_ASSOC)){
@ -329,7 +329,7 @@ or false on failure.
} }
/** /**
* main function used to delete rows by multiple key=>value pairs from Db table. * main function used to delete rows by multiple key=>value pairs from Db table.
* *
* @param string $table Database table to delete row in * @param string $table Database table to delete row in
* @param array $where Array with column=>values to select rows by * @param array $where Array with column=>values to select rows by
@ -343,7 +343,7 @@ or false on failure.
{ {
$query="DELETE"; $query="DELETE";
$query.= " FROM " . $table; $query.= " FROM " . $table;
if ($where!=null){ if ($where!=null){
$query.= " WHERE"; $query.= " WHERE";
foreach ($where as $key=>$value) { foreach ($where as $key=>$value) {
$query.= " ". $key . " = '" . $value . "' and"; $query.= " ". $key . " = '" . $value . "' and";
@ -358,10 +358,10 @@ or false on failure.
/** /**
* Function to do a custom query on database connection * Function to do a custom query on database connection
* *
* @param string $query Database query * @param string $query Database query
* @return mixed * @return mixed
* *
*/ */
public function customQuery($query) public function customQuery($query)
@ -370,14 +370,14 @@ or false on failure.
} }
/** /**
* Function to do a custom query on database connection * Function to do a custom query on database connection
* *
* @return int number of rows affected by last statement or 0 if database connection is not functional. * @return int number of rows affected by last statement or 0 if database connection is not functional.
* *
*/ */
public function rowCount() public function rowCount()
{ {
if($this->result) { if($this->result) {
$count=count($this->result->fetchAll()); $count=count($this->result->fetchAll());
$this->result->closeCursor(); $this->result->closeCursor();
return $count; return $count;
@ -387,8 +387,8 @@ or false on failure.
} }
/** /**
* helper function used to get rows from Db table in reversed order. * helper function used to get rows from Db table in reversed order.
* defaults to obtaining 1 row. * defaults to obtaining 1 row.
* *
* @param string $table Database table to update row in * @param string $table Database table to update row in
* @param string $key Column to select rows by * @param string $key Column to select rows by
@ -403,8 +403,8 @@ or false on failure.
} }
/** /**
* helper function used to get rows from Db table in standard order. * helper function used to get rows from Db table in standard order.
* defaults to obtaining 1 row. * defaults to obtaining 1 row.
* *
* @param string $table Database table to update row in * @param string $table Database table to update row in
* @param string $key Column to select rows by * @param string $key Column to select rows by
@ -417,7 +417,7 @@ or false on failure.
{ {
return Db::findBy($table, $key, $value, $nr); return Db::findBy($table, $key, $value, $nr);
} }
} }

View File

@ -13,13 +13,13 @@ $myLog = new Log($logname);
$db=new Db($baseParams['__YKVAL_DB_DSN__'], $db=new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
$logname . ':db'); $logname . ':db');
if (!$db->connect()) { if (!$db->connect()) {
$myLog->log(LOG_WARNING, "Could not connect to database"); $myLog->log(LOG_WARNING, "Could not connect to database");
exit(1); exit(1);
} }
$result = $db->customQuery("select id, active, created, secret, email, notes, otp from clients order by id"); $result = $db->customQuery("select id, active, created, secret, email, notes, otp from clients order by id");
while($row = $result->fetch(PDO::FETCH_ASSOC)){ while($row = $result->fetch(PDO::FETCH_ASSOC)){

View File

@ -13,13 +13,13 @@ $myLog = new Log($logname);
$db=new Db($baseParams['__YKVAL_DB_DSN__'], $db=new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
$logname . ':db'); $logname . ':db');
if (!$db->connect()) { if (!$db->connect()) {
$myLog->log(LOG_WARNING, "Could not connect to database"); $myLog->log(LOG_WARNING, "Could not connect to database");
exit(1); exit(1);
} }
$result=$db->customQuery("SELECT active, created, modified, yk_publicname, yk_counter, yk_use, yk_low, yk_high, nonce, notes FROM yubikeys ORDER BY yk_publicname"); $result=$db->customQuery("SELECT active, created, modified, yk_publicname, yk_counter, yk_use, yk_low, yk_high, nonce, notes FROM yubikeys ORDER BY yk_publicname");
while($row = $result->fetch(PDO::FETCH_ASSOC)){ while($row = $result->fetch(PDO::FETCH_ASSOC)){

View File

@ -13,26 +13,26 @@ $myLog = new Log($logname);
$db=new Db($baseParams['__YKVAL_DB_DSN__'], $db=new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
$logname . ':db'); $logname . ':db');
if (!$db->connect()) { if (!$db->connect()) {
$myLog->log(LOG_WARNING, "Could not connect to database"); $myLog->log(LOG_WARNING, "Could not connect to database");
error_log("Could not connect to database"); error_log("Could not connect to database");
exit(1); exit(1);
} }
while ($res=fgetcsv(STDIN, 0, "\t")) { while ($res=fgetcsv(STDIN, 0, "\t")) {
$params=array("id"=>$res[0], $params=array("id"=>$res[0],
"active"=>$res[1], "active"=>$res[1],
"created"=>$res[2], "created"=>$res[2],
"secret"=>$res[3], "secret"=>$res[3],
"email"=>$res[4], "email"=>$res[4],
"notes"=>$res[5], "notes"=>$res[5],
"otp"=>$res[6]); "otp"=>$res[6]);
$query="SELECT * FROM clients WHERE id='" . $params['id'] . "'"; $query="SELECT * FROM clients WHERE id='" . $params['id'] . "'";
$result=$db->customQuery($query); $result=$db->customQuery($query);
if(!$result->fetch(PDO::FETCH_ASSOC)) { if(!$result->fetch(PDO::FETCH_ASSOC)) {
@ -46,7 +46,7 @@ while ($res=fgetcsv(STDIN, 0, "\t")) {
"'" . $params['email'] . "'," . "'" . $params['email'] . "'," .
"'" . $params['notes'] . "'," . "'" . $params['notes'] . "'," .
"'" . $params['otp'] . "')"; "'" . $params['otp'] . "')";
if(!$db->customQuery($query)){ if(!$db->customQuery($query)){
$myLog->log(LOG_ERR, "Failed to insert new client with query " . $query); $myLog->log(LOG_ERR, "Failed to insert new client with query " . $query);
error_log("Failed to insert new client with query " . $query); error_log("Failed to insert new client with query " . $query);

View File

@ -13,29 +13,29 @@ $myLog = new Log($logname);
$db=new Db($baseParams['__YKVAL_DB_DSN__'], $db=new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
$logname . ':db'); $logname . ':db');
if (!$db->connect()) { if (!$db->connect()) {
$myLog->log(LOG_WARNING, "Could not connect to database"); $myLog->log(LOG_WARNING, "Could not connect to database");
error_log("Could not connect to database"); error_log("Could not connect to database");
exit(1); exit(1);
} }
while ($res=fgetcsv(STDIN, 0, "\t")) { while ($res=fgetcsv(STDIN, 0, "\t")) {
$params=array("active"=>$res[0], $params=array("active"=>$res[0],
"created"=>$res[1], "created"=>$res[1],
"modified"=>$res[2], "modified"=>$res[2],
"yk_publicname"=>$res[3], "yk_publicname"=>$res[3],
"yk_counter"=>$res[4], "yk_counter"=>$res[4],
"yk_use"=>$res[5], "yk_use"=>$res[5],
"yk_low"=>$res[6], "yk_low"=>$res[6],
"yk_high"=>$res[7], "yk_high"=>$res[7],
"nonce"=>$res[8], "nonce"=>$res[8],
"notes"=>$res[9]); "notes"=>$res[9]);
$query="SELECT * FROM yubikeys WHERE yk_publicname='" . $params['yk_publicname'] . "'"; $query="SELECT * FROM yubikeys WHERE yk_publicname='" . $params['yk_publicname'] . "'";
$result=$db->customQuery($query); $result=$db->customQuery($query);
if($result->fetch(PDO::FETCH_ASSOC)) { if($result->fetch(PDO::FETCH_ASSOC)) {
@ -52,13 +52,13 @@ while ($res=fgetcsv(STDIN, 0, "\t")) {
"WHERE yk_publicname='" . $params['yk_publicname'] . "' AND " . "WHERE yk_publicname='" . $params['yk_publicname'] . "' AND " .
"(".$params['yk_counter'].">yk_counter or (".$params['yk_counter']."=yk_counter and " . "(".$params['yk_counter'].">yk_counter or (".$params['yk_counter']."=yk_counter and " .
$params['yk_use'] . ">yk_use))"; $params['yk_use'] . ">yk_use))";
if(!$db->customQuery($query)) { if(!$db->customQuery($query)) {
$myLog->log(LOG_ERR, "Failed to update yk_publicname with query " . $query); $myLog->log(LOG_ERR, "Failed to update yk_publicname with query " . $query);
error_log("Failed to update yk_publicname with query " . $query); error_log("Failed to update yk_publicname with query " . $query);
exit(1); exit(1);
} }
} else { } else {
// We didn't have the yk_publicname in database so we need to do insert instead // We didn't have the yk_publicname in database so we need to do insert instead
$query="INSERT INTO yubikeys " . $query="INSERT INTO yubikeys " .
@ -73,7 +73,7 @@ while ($res=fgetcsv(STDIN, 0, "\t")) {
"'" . $params['yk_high'] . "'," . "'" . $params['yk_high'] . "'," .
"'" . $params['nonce'] . "'," . "'" . $params['nonce'] . "'," .
"'" . $params['notes'] . "')"; "'" . $params['notes'] . "')";
if(!$db->customQuery($query)){ if(!$db->customQuery($query)){
$myLog->log(LOG_ERR, "Failed to insert new yk_publicname with query " . $query); $myLog->log(LOG_ERR, "Failed to insert new yk_publicname with query " . $query);
error_log("Failed to insert new yk_publicname with query " . $query); error_log("Failed to insert new yk_publicname with query " . $query);

View File

@ -2,29 +2,29 @@
class Log class Log
{ {
function __construct($name='ykval') function __construct($name='ykval')
{ {
$this->name=$name; $this->name=$name;
$this->fields=array(); $this->fields=array();
$this->LOG_LEVELS = array(LOG_EMERG=>'LOG_EMERG', $this->LOG_LEVELS = array(LOG_EMERG=>'LOG_EMERG',
LOG_ALERT=>'LOG_ALERT', LOG_ALERT=>'LOG_ALERT',
LOG_CRIT=>'LOG_CRIT', LOG_CRIT=>'LOG_CRIT',
LOG_ERR=>'LOG_ERR', LOG_ERR=>'LOG_ERR',
LOG_WARNING=>'LOG_WARNING', LOG_WARNING=>'LOG_WARNING',
LOG_NOTICE=>'LOG_NOTICE', LOG_NOTICE=>'LOG_NOTICE',
LOG_INFO=>'LOG_INFO', LOG_INFO=>'LOG_INFO',
LOG_DEBUG=>'LOG_DEBUG'); LOG_DEBUG=>'LOG_DEBUG');
openlog("ykval", LOG_PID, LOG_LOCAL0); openlog("ykval", LOG_PID, LOG_LOCAL0);
} }
function addField($name, $value) function addField($name, $value)
{ {
$this->fields[$name]=$value; $this->fields[$name]=$value;
} }
function log($priority, $message, $arr=null){ function log($priority, $message, $arr=null){
if (is_array($arr)) { if (is_array($arr)) {
foreach($arr as $key=>$value){ foreach($arr as $key=>$value){
@ -36,13 +36,13 @@ class Log
foreach ($this->fields as $field=>$value) { foreach ($this->fields as $field=>$value) {
$msg_fields .= "[" . $value . "] "; $msg_fields .= "[" . $value . "] ";
} }
syslog($priority, syslog($priority,
$this->LOG_LEVELS[$priority] . ':' . $this->LOG_LEVELS[$priority] . ':' .
$this->name . ':' . $this->name . ':' .
$msg_fields . $msg_fields .
$message); $message);
} }
} }
?> ?>

View File

@ -4,8 +4,8 @@
set_include_path(get_include_path() . PATH_SEPARATOR . set_include_path(get_include_path() . PATH_SEPARATOR .
"/etc/ykval:/usr/share/ykval"); "/etc/ykval:/usr/share/ykval");
require_once 'ykval-synclib.php';
require_once 'ykval-config.php'; require_once 'ykval-config.php';
require_once 'ykval-synclib.php';
require_once 'ykval-log.php'; require_once 'ykval-log.php';
if ($argc==2 && strcmp($argv[1], "autoconf") == 0) { if ($argc==2 && strcmp($argv[1], "autoconf") == 0) {

View File

@ -1,4 +1,4 @@
<?php <?php
header("content-type: text/plain"); header("content-type: text/plain");

View File

@ -12,18 +12,18 @@ if ($argc==2 && strcmp($argv[1], "install")!=0) {
set_include_path(get_include_path() . PATH_SEPARATOR . $argv[1]); set_include_path(get_include_path() . PATH_SEPARATOR . $argv[1]);
} }
require_once "System/Daemon.php"; require_once "System/Daemon.php";
$appname="ykval-queue"; $appname="ykval-queue";
System_Daemon::setOption("appName", $appname); System_Daemon::setOption("appName", $appname);
System_Daemon::setOption("appDescription", "Yubico val-server sync daemon"); System_Daemon::setOption("appDescription", "Yubico val-server sync daemon");
System_Daemon::setOption("authorName", "olov@yubico.com"); System_Daemon::setOption("authorName", "olov@yubico.com");
System_Daemon::setOption("authorEmail", "olov@yubico.com"); System_Daemon::setOption("authorEmail", "olov@yubico.com");
if ($argc==2 && strcmp($argv[1], "install")==0) { if ($argc==2 && strcmp($argv[1], "install")==0) {
$autostart_path = System_Daemon::writeAutoRun(); $autostart_path = System_Daemon::writeAutoRun();
if ($autostart_path!=1){ if ($autostart_path!=1){
echo "Successfully created start script at " . $autostart_path . "\n"; echo "Successfully created start script at " . $autostart_path . "\n";
echo "To start daemon use: /etc/init.d/".$appname." start\n"; echo "To start daemon use: /etc/init.d/".$appname." start\n";
} else { } else {
@ -46,7 +46,7 @@ $sl = new SyncLib('ykval-queue:synclib');
$res==0; $res==0;
while ($res==0) { while ($res==0) {
$sl->reSync($baseParams['__YKVAL_SYNC_OLD_LIMIT__'], $sl->reSync($baseParams['__YKVAL_SYNC_OLD_LIMIT__'],
$baseParams['__YKVAL_SYNC_RESYNC_TIMEOUT__']); $baseParams['__YKVAL_SYNC_RESYNC_TIMEOUT__']);
$res=sleep($baseParams['__YKVAL_SYNC_INTERVAL__']); $res=sleep($baseParams['__YKVAL_SYNC_INTERVAL__']);
} }

View File

@ -26,7 +26,7 @@ if ($do != "enable" && $do != "disable") {
$db = new Db($baseParams['__YKVAL_DB_DSN__'], $db = new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
'ykval-revoke:db'); 'ykval-revoke:db');
if (!$db->connect()) { if (!$db->connect()) {
logdie("ERROR Database connect error"); logdie("ERROR Database connect error");

View File

@ -20,7 +20,7 @@ if (! $sync->isConnected()) {
exit; exit;
} }
# #
# Verify that request comes from valid server # Verify that request comes from valid server
# #
@ -77,7 +77,7 @@ $myLog->addField('otp', $syncParams['otp']);
$sync->addField('otp', $syncParams['otp']); $sync->addField('otp', $syncParams['otp']);
# #
# Verify correctness of input parameters # Verify correctness of input parameters
# #
foreach (array('modified') as $param) { foreach (array('modified') as $param) {
@ -106,27 +106,27 @@ foreach (array('yk_counter', 'yk_use', 'yk_high', 'yk_low') as $param) {
$yk_publicname = $syncParams['yk_publicname']; $yk_publicname = $syncParams['yk_publicname'];
$localParams = $sync->getLocalParams($yk_publicname); $localParams = $sync->getLocalParams($yk_publicname);
if (!$localParams) { if (!$localParams) {
$myLog->log(LOG_NOTICE, 'Invalid Yubikey ' . $yk_publicname); $myLog->log(LOG_NOTICE, 'Refusing sync of invalid Yubikey ' . $yk_publicname);
sendResp(S_BACKEND_ERROR, $apiKey); sendResp(S_BACKEND_ERROR, $apiKey);
exit; exit;
} }
if ($localParams['active'] != 1) { if ($localParams['active'] != 1) {
$myLog->log(LOG_NOTICE, 'De-activated Yubikey ' . $yk_publicname); $myLog->log(LOG_NOTICE, 'Refusing sync of de-activated Yubikey ' . $yk_publicname);
sendResp(S_BAD_OTP, $apiKey); sendResp(S_BAD_OTP, $apiKey);
exit; exit;
} }
/* Conditional update local database */ /* Conditional update local database */
$sync->updateDbCounters($syncParams); $sync->updateDbCounters($syncParams);
$myLog->log(LOG_DEBUG, 'Local params ' , $localParams); $myLog->log(LOG_DEBUG, 'Local params ' , $localParams);
$myLog->log(LOG_DEBUG, 'Sync request params ' , $syncParams); $myLog->log(LOG_DEBUG, 'Sync request params ' , $syncParams);
# #
# Compare sync and local counters and generate warnings according to # Compare sync and local counters and generate warnings according to
# #
# http://code.google.com/p/yubikey-val-server-php/wiki/ServerReplicationProtocol # http://code.google.com/p/yubikey-val-server-php/wiki/ServerReplicationProtocol
# #
@ -143,7 +143,7 @@ if ($sync->countersEqual($localParams, $syncParams)) {
$syncParams['nonce']==$localParams['nonce']) { $syncParams['nonce']==$localParams['nonce']) {
$myLog->log(LOG_NOTICE, 'Sync request unnessecarily sent'); $myLog->log(LOG_NOTICE, 'Sync request unnessecarily sent');
} }
if ($syncParams['modified']!=$localParams['modified'] && if ($syncParams['modified']!=$localParams['modified'] &&
$syncParams['nonce']==$localParams['nonce']) { $syncParams['nonce']==$localParams['nonce']) {
$deltaModified = $syncParams['modified'] - $localParams['modified']; $deltaModified = $syncParams['modified'] - $localParams['modified'];
@ -156,7 +156,7 @@ if ($sync->countersEqual($localParams, $syncParams)) {
} }
$extra=array('modified'=>$localParams['modified'], $extra=array('modified'=>$localParams['modified'],
'nonce'=>$localParams['nonce'], 'nonce'=>$localParams['nonce'],
'yk_publicname'=>$yk_publicname, 'yk_publicname'=>$yk_publicname,

View File

@ -19,10 +19,10 @@ class SyncLib
$this->db=new Db($baseParams['__YKVAL_DB_DSN__'], $this->db=new Db($baseParams['__YKVAL_DB_DSN__'],
$baseParams['__YKVAL_DB_USER__'], $baseParams['__YKVAL_DB_USER__'],
$baseParams['__YKVAL_DB_PW__'], $baseParams['__YKVAL_DB_PW__'],
$baseParams['__YKVAL_DB_OPTIONS__'], $baseParams['__YKVAL_DB_OPTIONS__'],
$logname . ':db'); $logname . ':db');
$this->isConnected=$this->db->connect(); $this->isConnected=$this->db->connect();
$this->server_nonce=md5(uniqid(rand())); $this->server_nonce=md5(uniqid(rand()));
} }
@ -32,7 +32,7 @@ class SyncLib
$this->db->addField($name, $value); $this->db->addField($name, $value);
} }
function isConnected() function isConnected()
{ {
return $this->isConnected; return $this->isConnected;
} }
@ -42,11 +42,11 @@ class SyncLib
$unix=strptime($db_time, '%F %H:%M:%S'); $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); 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) function UnixToDbTime($unix)
{ {
return date('Y-m-d H:i:s', $unix); return date('Y-m-d H:i:s', $unix);
} }
function getServer($index) function getServer($index)
{ {
@ -91,11 +91,11 @@ class SyncLib
return $out[0]; return $out[0];
} }
public function localParamsFromInfoString($info) public function localParamsFromInfoString($info)
{ {
$out=explode(",", $info); $out=explode(",", $info);
parse_str($out[1], $params); parse_str($out[1], $params);
return array('yk_counter'=>$params['local_counter'], return array('yk_counter'=>$params['local_counter'],
'yk_use'=>$params['local_use']); 'yk_use'=>$params['local_use']);
} }
@ -105,14 +105,14 @@ class SyncLib
$info=$this->createInfoString($otpParams, $localParams); $info=$this->createInfoString($otpParams, $localParams);
$this->otpParams = $otpParams; $this->otpParams = $otpParams;
$this->localParams = $localParams; $this->localParams = $localParams;
$queued=time(); $queued=time();
$res=True; $res=True;
foreach ($this->syncServers as $server) { foreach ($this->syncServers as $server) {
if(! $this->db->save('queue', array('queued'=>$queued, if(! $this->db->save('queue', array('queued'=>$queued,
'modified'=>$otpParams['modified'], 'modified'=>$otpParams['modified'],
'otp'=>$otpParams['otp'], 'otp'=>$otpParams['otp'],
'server'=>$server, 'server'=>$server,
'server_nonce'=>$this->server_nonce, 'server_nonce'=>$this->server_nonce,
'info'=>$info))) $res=False; 'info'=>$info))) $res=False;
@ -132,9 +132,9 @@ class SyncLib
if ($params) $logMsg .= ' modified=' . $params['modified'] . if ($params) $logMsg .= ' modified=' . $params['modified'] .
' nonce=' . $params['nonce'] . ' nonce=' . $params['nonce'] .
' yk_publicname=' . $params['yk_publicname'] . ' yk_publicname=' . $params['yk_publicname'] .
' yk_counter=' . $params['yk_counter'] . ' yk_counter=' . $params['yk_counter'] .
' yk_use=' . $params['yk_use'] . ' yk_use=' . $params['yk_use'] .
' yk_high=' . $params['yk_high'] . ' yk_high=' . $params['yk_high'] .
' yk_low=' . $params['yk_low']; ' yk_low=' . $params['yk_low'];
if ($this->myLog) $this->myLog->log($priority, $logMsg); if ($this->myLog) $this->myLog->log($priority, $logMsg);
else error_log("Warning: myLog uninitialized in ykval-synclib.php. Message is " . $logMsg); else error_log("Warning: myLog uninitialized in ykval-synclib.php. Message is " . $logMsg);
@ -147,7 +147,7 @@ class SyncLib
if (!$res) { if (!$res) {
$this->log(LOG_NOTICE, 'Discovered new identity ' . $yk_publicname); $this->log(LOG_NOTICE, 'Discovered new identity ' . $yk_publicname);
$this->db->save('yubikeys', array('active'=>1, $this->db->save('yubikeys', array('active'=>1,
'created'=>time(), 'created'=>time(),
'modified'=>-1, 'modified'=>-1,
'yk_publicname'=>$yk_publicname, 'yk_publicname'=>$yk_publicname,
@ -164,11 +164,11 @@ class SyncLib
'nonce'=>$res['nonce'], 'nonce'=>$res['nonce'],
'active'=>$res['active'], 'active'=>$res['active'],
'yk_publicname'=>$yk_publicname, 'yk_publicname'=>$yk_publicname,
'yk_counter'=>$res['yk_counter'], 'yk_counter'=>$res['yk_counter'],
'yk_use'=>$res['yk_use'], 'yk_use'=>$res['yk_use'],
'yk_high'=>$res['yk_high'], 'yk_high'=>$res['yk_high'],
'yk_low'=>$res['yk_low']); 'yk_low'=>$res['yk_low']);
$this->log(LOG_INFO, "yubikey found in db ", $localParams); $this->log(LOG_INFO, "yubikey found in db ", $localParams);
return $localParams; return $localParams;
} else { } else {
@ -230,18 +230,18 @@ class SyncLib
if (isset($params['yk_publicname'])) { if (isset($params['yk_publicname'])) {
$condition='('.$params['yk_counter'].'>yk_counter or ('.$params['yk_counter'].'=yk_counter and ' . $condition='('.$params['yk_counter'].'>yk_counter or ('.$params['yk_counter'].'=yk_counter and ' .
$params['yk_use'] . '>yk_use))' ; $params['yk_use'] . '>yk_use))' ;
if(! $this->db->conditionalUpdateBy('yubikeys', 'yk_publicname', $params['yk_publicname'], if(! $this->db->conditionalUpdateBy('yubikeys', 'yk_publicname', $params['yk_publicname'],
array('modified'=>$params['modified'], array('modified'=>$params['modified'],
'yk_counter'=>$params['yk_counter'], 'yk_counter'=>$params['yk_counter'],
'yk_use'=>$params['yk_use'], 'yk_use'=>$params['yk_use'],
'yk_low'=>$params['yk_low'], 'yk_low'=>$params['yk_low'],
'yk_high'=>$params['yk_high'], 'yk_high'=>$params['yk_high'],
'nonce'=>$params['nonce']), 'nonce'=>$params['nonce']),
$condition)) $condition))
{ {
$this->log(LOG_CRIT, 'failed to update internal DB with new counters'); $this->log(LOG_CRIT, 'failed to update internal DB with new counters');
return false; return false;
} else } else
{ {
if ($this->db->rowCount()>0) $this->log(LOG_INFO, "updated database ", $params); if ($this->db->rowCount()>0) $this->log(LOG_INFO, "updated database ", $params);
else $this->log(LOG_INFO, 'database not updated', $params); else $this->log(LOG_INFO, 'database not updated', $params);
@ -249,7 +249,7 @@ class SyncLib
} }
} else return false; } else return false;
} }
public function countersHigherThan($p1, $p2) public function countersHigherThan($p1, $p2)
{ {
if ($p1['yk_counter'] > $p2['yk_counter'] || if ($p1['yk_counter'] > $p2['yk_counter'] ||
@ -257,7 +257,7 @@ class SyncLib
$p1['yk_use'] > $p2['yk_use'])) return true; $p1['yk_use'] > $p2['yk_use'])) return true;
else return false; else return false;
} }
public function countersHigherThanOrEqual($p1, $p2) public function countersHigherThanOrEqual($p1, $p2)
{ {
if ($p1['yk_counter'] > $p2['yk_counter'] || if ($p1['yk_counter'] > $p2['yk_counter'] ||
@ -270,17 +270,17 @@ class SyncLib
return ($p1['yk_counter']==$p2['yk_counter']) && ($p1['yk_use']==$p2['yk_use']); return ($p1['yk_counter']==$p2['yk_counter']) && ($p1['yk_use']==$p2['yk_use']);
} }
public function deleteQueueEntry($answer) public function deleteQueueEntry($answer)
{ {
preg_match('/url=(.*)\?/', $answer, $out); preg_match('/url=(.*)\?/', $answer, $out);
$server=$out[1]; $server=$out[1];
$this->log(LOG_INFO, "deleting server=" . $server . $this->log(LOG_INFO, "deleting server=" . $server .
" modified=" . $this->otpParams['modified'] . " modified=" . $this->otpParams['modified'] .
" server_nonce=" . $this->server_nonce); " server_nonce=" . $this->server_nonce);
$this->db->deleteByMultiple('queue', $this->db->deleteByMultiple('queue',
array("modified"=>$this->otpParams['modified'], array("modified"=>$this->otpParams['modified'],
"server_nonce"=>$this->server_nonce, "server_nonce"=>$this->server_nonce,
'server'=>$server)); 'server'=>$server));
} }
@ -290,20 +290,20 @@ class SyncLib
/* Loop over all unique servers in queue */ /* Loop over all unique servers in queue */
$queued_limit=time()-$older_than; $queued_limit=time()-$older_than;
$res=$this->db->customQuery("select distinct server from queue WHERE queued < " . $queued_limit . " or queued is null"); $res=$this->db->customQuery("select distinct server from queue WHERE queued < " . $queued_limit . " or queued is null");
foreach ($res as $my_server) { foreach ($res as $my_server) {
$this->log(LOG_INFO, "Sending queue request to server on server " . $my_server['server']); $this->log(LOG_INFO, "Sending queue request to server on server " . $my_server['server']);
$res=$this->db->customQuery("select * from queue WHERE (queued < " . $queued_limit . " or queued is null) and server='" . $my_server['server'] . "'"); $res=$this->db->customQuery("select * from queue WHERE (queued < " . $queued_limit . " or queued is null) and server='" . $my_server['server'] . "'");
$ch = curl_init(); $ch = curl_init();
while ($entry=$res->fetch(PDO::FETCH_ASSOC)) { while ($entry=$res->fetch(PDO::FETCH_ASSOC)) {
$this->log(LOG_INFO, "server=" . $entry['server'] . " , info=" . $entry['info']); $this->log(LOG_INFO, "server=" . $entry['server'] . " , info=" . $entry['info']);
$url=$entry['server'] . $url=$entry['server'] .
"?otp=" . $entry['otp'] . "?otp=" . $entry['otp'] .
"&modified=" . $entry['modified'] . "&modified=" . $entry['modified'] .
"&" . $this->otpPartFromInfoString($entry['info']); "&" . $this->otpPartFromInfoString($entry['info']);
/* Send out sync request */ /* Send out sync request */
$this->log(LOG_DEBUG, 'url is ' . $url); $this->log(LOG_DEBUG, 'url is ' . $url);
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
@ -313,101 +313,103 @@ class SyncLib
curl_setopt($ch, CURLOPT_FAILONERROR, true); curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$response = curl_exec($ch); $response = curl_exec($ch);
if ($response==False) { if ($response==False) {
$this->log(LOG_NOTICE, 'Timeout. Stopping queue resync for server ' . $my_server['server']); $this->log(LOG_NOTICE, 'Timeout. Stopping queue resync for server ' . $my_server['server']);
break; break;
} }
if (preg_match("/status=OK/", $response)) { if (preg_match("/status=OK/", $response)) {
$resParams=$this->parseParamsFromMultiLineString($response); $resParams=$this->parseParamsFromMultiLineString($response);
$this->log(LOG_DEBUG, "response contains ", $resParams); $this->log(LOG_DEBUG, "response contains ", $resParams);
/* Update database counters */ /* Update database counters */
$this->updateDbCounters($resParams); $this->updateDbCounters($resParams);
/* Retrieve info from entry info string */ /* Retrieve info from entry info string */
$validationParams=$this->localParamsFromInfoString($entry['info']); $validationParams=$this->localParamsFromInfoString($entry['info']);
$otpParams=$this->otpParamsFromInfoString($entry['info']); $otpParams=$this->otpParamsFromInfoString($entry['info']);
$localParams=$this->getLocalParams($otpParams['yk_publicname']); $localParams=$this->getLocalParams($otpParams['yk_publicname']);
$this->log(LOG_DEBUG, "validation params: ", $validationParams); $this->log(LOG_DEBUG, "validation params: ", $validationParams);
$this->log(LOG_DEBUG, "OTP params: ", $otpParams); $this->log(LOG_DEBUG, "OTP params: ", $otpParams);
/* Check for warnings */ /* Check for warnings */
if ($this->countersHigherThan($validationParams, $resParams)) { if ($this->countersHigherThan($validationParams, $resParams)) {
$this->log(LOG_NOTICE, "Remote server out of sync compared to counters at validation request time. "); $this->log(LOG_NOTICE, "Remote server out of sync compared to counters at validation request time. ");
} }
if ($this->countersHigherThan($resParams, $validationParams)) { if ($this->countersHigherThan($resParams, $validationParams)) {
$this->log(LOG_NOTICE, "Local server out of sync compared to counters at validation request time. "); $this->log(LOG_NOTICE, "Local server out of sync compared to counters at validation request time. ");
} }
if ($this->countersHigherThan($localParams, $resParams)) { if ($this->countersHigherThan($localParams, $resParams)) {
$this->log(LOG_WARNING, "Remote server out of sync compared to current local counters. "); $this->log(LOG_WARNING, "Remote server out of sync compared to current local counters. ");
} }
if ($this->countersHigherThan($resParams, $localParams)) { if ($this->countersHigherThan($resParams, $localParams)) {
$this->log(LOG_WARNING, "Local server out of sync compared to current local counters. Local server updated. "); $this->log(LOG_WARNING, "Local server out of sync compared to current local counters. Local server updated. ");
} }
if ($this->countersHigherThan($resParams, $otpParams)) { if ($this->countersHigherThan($resParams, $otpParams)) {
$this->log(LOG_ERR, "Remote server has higher counters than OTP. This response would have marked the OTP as invalid. "); $this->log(LOG_ERR, "Remote server has higher counters than OTP. This response would have marked the OTP as invalid. ");
} }
elseif ($this->countersEqual($resParams, $otpParams) && elseif ($this->countersEqual($resParams, $otpParams) &&
$resParams['nonce']!=$otpParams['nonce']) { $resParams['nonce']!=$otpParams['nonce']) {
$this->log(LOG_ERR, "Remote server has equal counters as OTP and nonce differs. This response would have marked the OTP as invalid."); $this->log(LOG_ERR, "Remote server has equal counters as OTP and nonce differs. This response would have marked the OTP as invalid.");
} }
/* Deletion */ /* Deletion */
$this->log(LOG_INFO, 'deleting queue entry with modified=' . $entry['modified'] . $this->log(LOG_INFO, 'deleting queue entry with modified=' . $entry['modified'] .
' server_nonce=' . $entry['server_nonce'] . ' server_nonce=' . $entry['server_nonce'] .
' server=' . $entry['server']); ' server=' . $entry['server']);
$this->db->deleteByMultiple('queue', $this->db->deleteByMultiple('queue',
array("modified"=>$entry['modified'], array("modified"=>$entry['modified'],
"server_nonce"=>$entry['server_nonce'], "server_nonce"=>$entry['server_nonce'],
'server'=>$entry['server'])); 'server'=>$entry['server']));
} else {
$this->log(LOG_ERR, "Remote server refused our sync request. Check remote server logs.");
} }
} /* End of loop over each queue entry for a server */ } /* End of loop over each queue entry for a server */
curl_close($ch); curl_close($ch);
$res->closeCursor(); $res->closeCursor();
} /* End of loop over each distinct server in queue */ } /* End of loop over each distinct server in queue */
return true; return true;
} }
public function sync($ans_req, $timeout=1) public function sync($ans_req, $timeout=1)
{ {
/* /*
Construct URLs Construct URLs
*/ */
$urls=array(); $urls=array();
$res=$this->db->findByMultiple('queue', array("modified"=>$this->otpParams['modified'], "server_nonce"=>$this->server_nonce)); $res=$this->db->findByMultiple('queue', array("modified"=>$this->otpParams['modified'], "server_nonce"=>$this->server_nonce));
foreach ($res as $row) { foreach ($res as $row) {
$urls[]=$row['server'] . $urls[]=$row['server'] .
"?otp=" . $row['otp'] . "?otp=" . $row['otp'] .
"&modified=" . $row['modified'] . "&modified=" . $row['modified'] .
"&" . $this->otpPartFromInfoString($row['info']); "&" . $this->otpPartFromInfoString($row['info']);
} }
/* /*
Send out requests Send out requests
*/ */
$ans_arr=$this->retrieveURLasync($urls, $ans_req, $timeout); $ans_arr=$this->retrieveURLasync($urls, $ans_req, $timeout);
if (!is_array($ans_arr)) { if (!is_array($ans_arr)) {
$this->log(LOG_WARNING, 'No responses from validation server pool'); $this->log(LOG_WARNING, 'No responses from validation server pool');
$ans_arr=array(); $ans_arr=array();
} }
/* /*
Parse responses Parse responses
*/ */
$localParams = $this->localParams; $localParams = $this->localParams;
$this->answers = count($ans_arr); $this->answers = count($ans_arr);
$this->valid_answers = 0; $this->valid_answers = 0;
foreach ($ans_arr as $answer){ foreach ($ans_arr as $answer){
@ -418,71 +420,71 @@ class SyncLib
$this->log(LOG_DEBUG, "OTP contains " , $this->otpParams); $this->log(LOG_DEBUG, "OTP contains " , $this->otpParams);
/* Update internal DB (conditional) */ /* Update internal DB (conditional) */
$this->updateDbCounters($resParams); $this->updateDbCounters($resParams);
/* Check for warnings /* Check for warnings
See http://code.google.com/p/yubikey-val-server-php/wiki/ServerReplicationProtocol See http://code.google.com/p/yubikey-val-server-php/wiki/ServerReplicationProtocol
NOTE: We use localParams for validationParams comparison since they are actually the NOTE: We use localParams for validationParams comparison since they are actually the
same in this situation and we have them at hand. same in this situation and we have them at hand.
*/ */
if ($this->countersHigherThan($localParams, $resParams)) { if ($this->countersHigherThan($localParams, $resParams)) {
$this->log(LOG_NOTICE, "Remote server out of sync"); $this->log(LOG_NOTICE, "Remote server out of sync");
} }
if ($this->countersHigherThan($resParams, $localParams)) { if ($this->countersHigherThan($resParams, $localParams)) {
$this->log(LOG_NOTICE, "Local server out of sync"); $this->log(LOG_NOTICE, "Local server out of sync");
} }
if ($this->CountersEqual($resParams, $localParams) && if ($this->CountersEqual($resParams, $localParams) &&
$resParams['nonce']!=$localParams['nonce']) { $resParams['nonce']!=$localParams['nonce']) {
$this->log(LOG_NOTICE, "Servers out of sync. Nonce differs. "); $this->log(LOG_NOTICE, "Servers out of sync. Nonce differs. ");
} }
if ($this->CountersEqual($resParams, $localParams) && if ($this->CountersEqual($resParams, $localParams) &&
$resParams['modified']!=$localParams['modified']) { $resParams['modified']!=$localParams['modified']) {
$this->log(LOG_NOTICE, "Servers out of sync. Modified differs. "); $this->log(LOG_NOTICE, "Servers out of sync. Modified differs. ");
} }
if ($this->countersHigherThan($resParams, $this->otpParams)){ if ($this->countersHigherThan($resParams, $this->otpParams)){
$this->log(LOG_WARNING, 'OTP is replayed. Sync response counters higher than OTP counters.'); $this->log(LOG_WARNING, 'OTP is replayed. Sync response counters higher than OTP counters.');
} }
elseif ($this->countersEqual($resParams, $this->otpParams) && elseif ($this->countersEqual($resParams, $this->otpParams) &&
$resParams['nonce']!=$this->otpParams['nonce']) { $resParams['nonce']!=$this->otpParams['nonce']) {
$this->log(LOG_WARNING, 'OTP is replayed. Sync response counters equal to OTP counters and nonce differs.'); $this->log(LOG_WARNING, 'OTP is replayed. Sync response counters equal to OTP counters and nonce differs.');
} else { } else {
/* The answer is ok since a REPLAY was not indicated */ /* The answer is ok since a REPLAY was not indicated */
$this->valid_answers++; $this->valid_answers++;
} }
/* Delete entry from table */ /* Delete entry from table */
$this->deleteQueueEntry($answer); $this->deleteQueueEntry($answer);
} }
/* /*
NULL queued_time for remaining entries in queue, to allow NULL queued_time for remaining entries in queue, to allow
daemon to take care of them as soon as possible. */ daemon to take care of them as soon as possible. */
$this->db->updateBy('queue', 'server_nonce', $this->server_nonce,
array('queued'=>NULL));
/* Return true if valid answers equals required answers. $this->db->updateBy('queue', 'server_nonce', $this->server_nonce,
Since we only obtain the required amount of answers from array('queued'=>NULL));
retrieveAsync this indicates that all answers were actually valid.
/* 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. */ Otherwise, return false. */
if ($this->valid_answers==$ans_req) return True; if ($this->valid_answers==$ans_req) return True;
else return False; else return False;
@ -507,7 +509,7 @@ class SyncLib
The request are sent asynchronously. Some of the URLs can fail The request are sent asynchronously. Some of the URLs can fail
with unknown host, connection errors, or network timeout, but as with unknown host, connection errors, or network timeout, but as
long as one of the URLs given work, data will be returned. If all 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 URLs fail, data from some URL that did not match parameter $match
(defaults to ^OK) is returned, or if all URLs failed, false. (defaults to ^OK) is returned, or if all URLs failed, false.
*/ */
function retrieveURLasync ($urls, $ans_req=1, $timeout=1.0) { function retrieveURLasync ($urls, $ans_req=1, $timeout=1.0) {
@ -517,26 +519,26 @@ class SyncLib
foreach ($urls as $id => $url) { foreach ($urls as $id => $url) {
$this->log(LOG_DEBUG, "url in retrieveURLasync is " . $url); $this->log(LOG_DEBUG, "url in retrieveURLasync is " . $url);
$handle = curl_init(); $handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $url); curl_setopt($handle, CURLOPT_URL, $url);
curl_setopt($handle, CURLOPT_USERAGENT, "YK-VAL"); curl_setopt($handle, CURLOPT_USERAGENT, "YK-VAL");
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLOPT_FAILONERROR, true); curl_setopt($handle, CURLOPT_FAILONERROR, true);
curl_setopt($handle, CURLOPT_TIMEOUT, $timeout); curl_setopt($handle, CURLOPT_TIMEOUT, $timeout);
curl_multi_add_handle($mh, $handle); curl_multi_add_handle($mh, $handle);
$ch[$handle] = $handle; $ch[$handle] = $handle;
} }
$str = false; $str = false;
$ans_count = 0; $ans_count = 0;
$ans_arr = array(); $ans_arr = array();
do { do {
while (($mrc = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM) while (($mrc = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM)
; ;
while ($info = curl_multi_info_read($mh)) { while ($info = curl_multi_info_read($mh)) {
debug ("YK-KSM multi", $info); debug ("YK-KSM multi", $info);
if ($info['result'] == CURLE_OK) { if ($info['result'] == CURLE_OK) {
@ -549,27 +551,27 @@ class SyncLib
$ans_count++; $ans_count++;
$ans_arr[]="url=" . $cinfo['url'] . "\n" . $str; $ans_arr[]="url=" . $cinfo['url'] . "\n" . $str;
} }
if ($ans_count >= $ans_req) { if ($ans_count >= $ans_req) {
foreach ($ch as $h) { foreach ($ch as $h) {
curl_multi_remove_handle ($mh, $h); curl_multi_remove_handle ($mh, $h);
curl_close ($h); curl_close ($h);
} }
curl_multi_close ($mh); curl_multi_close ($mh);
return $ans_arr; return $ans_arr;
} }
curl_multi_remove_handle ($mh, $info['handle']); curl_multi_remove_handle ($mh, $info['handle']);
curl_close ($info['handle']); curl_close ($info['handle']);
unset ($ch[$info['handle']]); unset ($ch[$info['handle']]);
} }
curl_multi_select ($mh); curl_multi_select ($mh);
} }
} while($active); } while($active);
foreach ($ch as $h) { foreach ($ch as $h) {
curl_multi_remove_handle ($mh, $h); curl_multi_remove_handle ($mh, $h);
curl_close ($h); curl_close ($h);
@ -579,7 +581,7 @@ class SyncLib
if ($ans_count>0) return $ans_arr; if ($ans_count>0) return $ans_arr;
else return $str; else return $str;
} }
} }
?> ?>

View File

@ -47,12 +47,12 @@ $myLog->addField('otp', $otp);
if ($protocol_version>=2.0) { if ($protocol_version>=2.0) {
$sl = getHttpVal('sl', ''); $sl = getHttpVal('sl', '');
$timeout = getHttpVal('timeout', ''); $timeout = getHttpVal('timeout', '');
$nonce = getHttpVal('nonce', ''); $nonce = getHttpVal('nonce', '');
/* Add nonce to response parameters */ /* Add nonce to response parameters */
$extra['nonce']= $nonce; $extra['nonce']= $nonce;
/* Nonce is required from protocol 2.0 */ /* Nonce is required from protocol 2.0 */
if(!$nonce) { if(!$nonce) {
$myLog->log(LOG_NOTICE, 'Nonce is missing and protocol version >= 2.0'); $myLog->log(LOG_NOTICE, 'Nonce is missing and protocol version >= 2.0');
@ -62,15 +62,15 @@ if ($protocol_version>=2.0) {
} }
/* Sanity check HTTP parameters /* Sanity check HTTP parameters
* otp: one-time password * otp: one-time password
* id: client id * id: client id
* timeout: timeout in seconds to wait for external answers, optional: if absent the server decides * 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 * 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 * 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) * h: signature (optional)
* timestamp: requests timestamp/counters in response * timestamp: requests timestamp/counters in response
*/ */
@ -129,7 +129,7 @@ if (isset($nonce) && (strlen($nonce) < 16 || strlen($nonce) > 40)) {
sendResp(S_MISSING_PARAMETER); sendResp(S_MISSING_PARAMETER);
exit; exit;
} }
if ($sl && (preg_match("/^[0-9]+$/", $sl)==0 || ($sl<0 || $sl>100))) { if ($sl && (preg_match("/^[0-9]+$/", $sl)==0 || ($sl<0 || $sl>100))) {
$myLog->log(LOG_NOTICE, 'SL is provided but not correct'); $myLog->log(LOG_NOTICE, 'SL is provided but not correct');
sendResp(S_MISSING_PARAMETER); sendResp(S_MISSING_PARAMETER);
@ -137,7 +137,7 @@ if ($sl && (preg_match("/^[0-9]+$/", $sl)==0 || ($sl<0 || $sl>100))) {
} }
// NOTE: Timestamp parameter is not checked since current protocol says that 1 means request timestamp // NOTE: Timestamp parameter is not checked since current protocol says that 1 means request timestamp
// and anything else is discarded. // and anything else is discarded.
//// Get Client info from DB //// Get Client info from DB
// //
@ -150,7 +150,7 @@ if ($client <= 0) {
/* Initialize the sync library. Strive to use this instead of custom /* Initialize the sync library. Strive to use this instead of custom
DB requests, custom comparisons etc */ DB requests, custom comparisons etc */
$sync = new SyncLib('ykval-verify:synclib'); $sync = new SyncLib('ykval-verify:synclib');
$sync->addField('ip', $_SERVER['REMOTE_ADDR']); $sync->addField('ip', $_SERVER['REMOTE_ADDR']);
$sync->addField('otp', $otp); $sync->addField('otp', $otp);
@ -194,11 +194,11 @@ if ($h != '') {
} }
} }
/* We need to add necessary parameters not available at earlier protocols after signature is computed. /* We need to add necessary parameters not available at earlier protocols after signature is computed.
*/ */
if ($protocol_version<2.0) { if ($protocol_version<2.0) {
/* We need to create a nonce manually here */ /* We need to create a nonce manually here */
$nonce = md5(uniqid(rand())); $nonce = md5(uniqid(rand()));
$myLog->log(LOG_INFO, 'protocol version below 2.0. Created nonce ' . $nonce); $myLog->log(LOG_INFO, 'protocol version below 2.0. Created nonce ' . $nonce);
} }
@ -239,13 +239,13 @@ if ($localParams['active'] != 1) {
/* Build OTP params */ /* Build OTP params */
$otpParams=array('modified'=>time(), $otpParams=array('modified'=>time(),
'otp'=>$otp, 'otp'=>$otp,
'nonce'=>$nonce, 'nonce'=>$nonce,
'yk_publicname'=>$devId, 'yk_publicname'=>$devId,
'yk_counter'=>$otpinfo['session_counter'], 'yk_counter'=>$otpinfo['session_counter'],
'yk_use'=>$otpinfo['session_use'], 'yk_use'=>$otpinfo['session_use'],
'yk_high'=>$otpinfo['high'], 'yk_high'=>$otpinfo['high'],
'yk_low'=>$otpinfo['low']); 'yk_low'=>$otpinfo['low']);
@ -257,7 +257,7 @@ if ($sync->countersEqual($localParams, $otpParams) &&
exit; exit;
} }
/* Check the OTP counters against local db */ /* Check the OTP counters against local db */
if ($sync->countersHigherThanOrEqual($localParams, $otpParams)) { if ($sync->countersHigherThanOrEqual($localParams, $otpParams)) {
$sync->log(LOG_WARNING, 'replayed OTP: Local counters higher'); $sync->log(LOG_WARNING, 'replayed OTP: Local counters higher');
$sync->log(LOG_WARNING, 'replayed OTP: Local counters ', $localParams); $sync->log(LOG_WARNING, 'replayed OTP: Local counters ', $localParams);
@ -289,7 +289,7 @@ if ($req_answers>0) {
$nr_answers=$sync->getNumberOfAnswers(); $nr_answers=$sync->getNumberOfAnswers();
$nr_valid_answers=$sync->getNumberOfValidAnswers(); $nr_valid_answers=$sync->getNumberOfValidAnswers();
$sl_success_rate=floor(100.0 * $nr_valid_answers / $nr_servers); $sl_success_rate=floor(100.0 * $nr_valid_answers / $nr_servers);
} else { } else {
$syncres=true; $syncres=true;
$nr_answers=0; $nr_answers=0;
@ -305,7 +305,7 @@ $myLog->log(LOG_INFO, "ykval-verify:notice:synclevel=" . $sl .
" timeout=" . $timeout); " timeout=" . $timeout);
if($syncres==False) { if($syncres==False) {
/* sync returned false, indicating that /* sync returned false, indicating that
either at least 1 answer marked OTP as invalid or either at least 1 answer marked OTP as invalid or
there were not enough answers */ there were not enough answers */
$myLog->log(LOG_WARNING, "ykval-verify:notice:Sync failed"); $myLog->log(LOG_WARNING, "ykval-verify:notice:Sync failed");
@ -319,7 +319,7 @@ if($syncres==False) {
} }
} }
/* Recreate parameters to make phising test work out /* Recreate parameters to make phising test work out
TODO: use timefunctionality in deltatime library instead */ TODO: use timefunctionality in deltatime library instead */
$sessionCounter = $otpParams['yk_counter']; $sessionCounter = $otpParams['yk_counter'];
$sessionUse = $otpParams['yk_use']; $sessionUse = $otpParams['yk_use'];
@ -345,8 +345,8 @@ if ($sessionCounter == $seenSessionCounter && $sessionUse > $seenSessionUse) {
$elapsed = $now - $lastTime; $elapsed = $now - $lastTime;
$deviation = abs($elapsed - $tsDelta); $deviation = abs($elapsed - $tsDelta);
// Time delta server might verify multiple OTPS in a row. In such case validation server doesn't // 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. // have time to tick a whole second and we need to avoid division by zero.
if ($elapsed != 0) { if ($elapsed != 0) {
$percent = $deviation/$elapsed; $percent = $deviation/$elapsed;
} else { } else {