2008-09-26 05:21:11 +02:00
|
|
|
<?php
|
2009-03-11 02:42:23 +01:00
|
|
|
|
2008-09-26 05:21:11 +02:00
|
|
|
define('S_OK', 'OK');
|
|
|
|
define('S_BAD_OTP', 'BAD_OTP');
|
|
|
|
define('S_REPLAYED_OTP', 'REPLAYED_OTP');
|
2009-03-11 01:19:59 +01:00
|
|
|
define('S_DELAYED_OTP', 'DELAYED_OTP');
|
2008-09-26 05:21:11 +02:00
|
|
|
define('S_BAD_SIGNATURE', 'BAD_SIGNATURE');
|
|
|
|
define('S_MISSING_PARAMETER', 'MISSING_PARAMETER');
|
2009-03-10 23:01:46 +01:00
|
|
|
define('S_NO_SUCH_CLIENT', 'NO_SUCH_CLIENT');
|
2008-09-26 05:21:11 +02:00
|
|
|
define('S_OPERATION_NOT_ALLOWED', 'OPERATION_NOT_ALLOWED');
|
|
|
|
define('S_BACKEND_ERROR', 'BACKEND_ERROR');
|
2009-03-11 02:42:23 +01:00
|
|
|
|
|
|
|
define('TS_SEC', 1/8);
|
2009-03-11 01:26:57 +01:00
|
|
|
define('TS_REL_TOLERANCE', 0.3);
|
|
|
|
define('TS_ABS_TOLERANCE', 20);
|
2008-09-26 05:21:11 +02:00
|
|
|
|
2009-03-18 12:00:48 +01:00
|
|
|
define('TOKEN_LEN', 32);
|
2009-03-10 23:50:35 +01:00
|
|
|
|
|
|
|
function unescape($s) {
|
|
|
|
return str_replace('\\', "", $s);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getHttpVal($key, $defaultVal) {
|
|
|
|
$val = $defaultVal;
|
|
|
|
if (array_key_exists($key, $_GET)) {
|
|
|
|
$val = $_GET[$key];
|
|
|
|
} else if (array_key_exists($key, $_POST)) {
|
|
|
|
$val = $_POST[$key];
|
|
|
|
}
|
|
|
|
$v = unescape(trim($val));
|
|
|
|
return $v;
|
|
|
|
}
|
|
|
|
|
2009-03-11 03:06:02 +01:00
|
|
|
function query($conn, $q) {
|
2009-04-27 19:57:24 +02:00
|
|
|
debug('SQL query: ' . $q);
|
2009-03-11 03:06:02 +01:00
|
|
|
$result = mysql_query($q, $conn);
|
|
|
|
if (!$result) {
|
2009-05-06 16:40:25 +02:00
|
|
|
debug("SQL query error: " . mysql_error());
|
|
|
|
return;
|
2009-03-11 03:06:02 +01:00
|
|
|
}
|
|
|
|
return $result;
|
2009-03-10 23:50:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function mysql_quote($value) {
|
|
|
|
return "'" . mysql_real_escape_string($value) . "'";
|
|
|
|
}
|
|
|
|
|
2009-04-27 19:57:24 +02:00
|
|
|
function debug() {
|
|
|
|
$str = "";
|
|
|
|
foreach (func_get_args() as $msg)
|
|
|
|
{
|
|
|
|
if (is_array($msg)) {
|
|
|
|
foreach($msg as $key => $value){
|
|
|
|
$str .= "$key=$value ";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$str .= $msg . " ";
|
|
|
|
}
|
2009-03-11 01:54:19 +01:00
|
|
|
}
|
|
|
|
error_log($str);
|
2008-09-26 05:21:11 +02:00
|
|
|
}
|
|
|
|
|
2009-03-10 20:01:07 +01:00
|
|
|
// Return eg. 2008-11-21T06:11:55Z0711
|
2008-11-21 07:41:13 +01:00
|
|
|
//
|
2008-09-27 11:04:49 +02:00
|
|
|
function getUTCTimeStamp() {
|
|
|
|
date_default_timezone_set('UTC');
|
2009-03-10 20:01:07 +01:00
|
|
|
$tiny = substr(microtime(false), 2, 3);
|
|
|
|
return date('Y-m-d\TH:i:s\Z0', time()) . $tiny;
|
2008-09-27 11:04:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sign a http query string in the array of key-value pairs
|
|
|
|
// return b64 encoded hmac hash
|
2009-03-11 03:06:02 +01:00
|
|
|
function sign($a, $apiKey) {
|
2008-09-27 11:04:49 +02:00
|
|
|
ksort($a);
|
|
|
|
$qs = '';
|
|
|
|
$n = count($a);
|
|
|
|
$i = 0;
|
|
|
|
foreach (array_keys($a) as $key) {
|
2008-11-21 22:41:26 +01:00
|
|
|
$qs .= trim($key).'='.trim($a[$key]);
|
2008-09-27 11:04:49 +02:00
|
|
|
if (++$i < $n) {
|
|
|
|
$qs .= '&';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// the TRUE at the end states we want the raw value, not hexadecimal form
|
|
|
|
$hmac = hash_hmac('sha1', utf8_encode($qs), $apiKey, true);
|
|
|
|
$hmac = base64_encode($hmac);
|
2009-03-11 02:53:38 +01:00
|
|
|
|
|
|
|
debug('SIGN: ' . $qs . ' H=' . $hmac);
|
|
|
|
|
2008-09-27 11:04:49 +02:00
|
|
|
return $hmac;
|
|
|
|
|
2008-10-08 08:29:19 +02:00
|
|
|
} // sign an array of query string
|
2008-09-27 11:04:49 +02:00
|
|
|
|
2009-03-11 02:37:07 +01:00
|
|
|
function hex2b64 ($hex_str) {
|
|
|
|
$bin = pack("H*", $hex_str);
|
|
|
|
return base64_encode($bin);
|
2009-03-10 23:50:35 +01:00
|
|
|
}
|
|
|
|
|
2009-03-11 02:37:07 +01:00
|
|
|
function modhex2b64 ($modhex_str) {
|
|
|
|
$hex_str = strtr ($modhex_str, "cbdefghijklnrtuv", "0123456789abcdef");
|
|
|
|
return hex2b64($hex_str);
|
2009-03-10 23:50:35 +01:00
|
|
|
}
|
|
|
|
|
2009-04-27 19:57:24 +02:00
|
|
|
// 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 ^OK is returned,
|
|
|
|
// or if all URLs failed, false.
|
|
|
|
function retrieveURLasync ($urls) {
|
|
|
|
$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;
|
|
|
|
|
|
|
|
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']);
|
|
|
|
|
|
|
|
if (preg_match("/^OK/", $str)) {
|
|
|
|
$error = curl_error ($info['handle']);
|
|
|
|
$errno = curl_errno ($info['handle']);
|
|
|
|
$info = curl_getinfo ($info['handle']);
|
|
|
|
debug("YK-KSM errno/error: " . $errno . "/" . $error, $info);
|
|
|
|
|
|
|
|
foreach ($ch as $h) {
|
|
|
|
curl_multi_remove_handle ($mh, $h);
|
|
|
|
curl_close ($h);
|
|
|
|
}
|
|
|
|
curl_multi_close ($mh);
|
|
|
|
|
|
|
|
return $str;
|
|
|
|
}
|
2009-03-11 02:37:07 +01:00
|
|
|
|
2009-04-27 19:57:24 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// $otp: A yubikey OTP
|
|
|
|
function KSMdecryptOTP($urls) {
|
|
|
|
$ret = array();
|
|
|
|
$response = retrieveURLasync ($urls);
|
|
|
|
if ($response) {
|
|
|
|
debug("YK-KSM response: " . $response);
|
|
|
|
}
|
2009-03-11 02:37:07 +01:00
|
|
|
if (sscanf ($response,
|
2009-03-18 16:16:18 +01:00
|
|
|
"OK counter=%04x low=%04x high=%02x use=%02x",
|
2009-03-18 16:21:50 +01:00
|
|
|
$ret["session_counter"], $ret["low"], $ret["high"],
|
|
|
|
$ret["session_use"]) != 4) {
|
2009-03-11 02:37:07 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return $ret;
|
|
|
|
} // End decryptOTP
|
2009-03-10 23:50:35 +01:00
|
|
|
|
|
|
|
// $devId: The first 12 chars from the OTP
|
2009-03-11 03:06:02 +01:00
|
|
|
function getAuthData($conn, $devId) {
|
2009-05-06 16:42:44 +02:00
|
|
|
$publicName = modhex2b64($devId);
|
2009-03-18 21:44:14 +01:00
|
|
|
$stmt =
|
2009-05-06 16:41:51 +02:00
|
|
|
'SELECT id, active, counter, sessionUse, low, high, accessed '.
|
2009-03-18 21:44:14 +01:00
|
|
|
'FROM yubikeys '.
|
2009-05-06 16:42:44 +02:00
|
|
|
'WHERE publicName='.mysql_quote($publicName);
|
2009-03-11 03:06:02 +01:00
|
|
|
$r = query($conn, $stmt);
|
2009-03-10 23:50:35 +01:00
|
|
|
if (mysql_num_rows($r) > 0) {
|
|
|
|
$row = mysql_fetch_assoc($r);
|
|
|
|
mysql_free_result($r);
|
|
|
|
return $row;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
} // End getAuthData
|
|
|
|
|
2009-04-01 18:09:29 +02:00
|
|
|
function addNewKey($conn, $devId) {
|
2009-05-06 16:42:44 +02:00
|
|
|
$publicName = modhex2b64($devId);
|
|
|
|
$stmt = 'INSERT INTO yubikeys (active, created, publicName, counter) '.
|
|
|
|
'VALUES (true, NOW(), ' . mysql_quote($publicName) . ', 0)';
|
2009-04-01 18:09:29 +02:00
|
|
|
$r = query($conn, $stmt);
|
|
|
|
}
|
|
|
|
|
2009-03-10 23:50:35 +01:00
|
|
|
// $clientId: The decimal client identity
|
2009-03-11 03:06:02 +01:00
|
|
|
function getClientData($conn, $clientId) {
|
2009-03-18 21:44:14 +01:00
|
|
|
$stmt =
|
2009-05-06 15:20:40 +02:00
|
|
|
'SELECT id, secret '.
|
2009-03-18 21:44:14 +01:00
|
|
|
'FROM clients '.
|
|
|
|
'WHERE active AND id='.mysql_quote($clientId);
|
2009-03-11 03:06:02 +01:00
|
|
|
$r = query($conn, $stmt);
|
2009-03-10 23:50:35 +01:00
|
|
|
if (mysql_num_rows($r) > 0) {
|
|
|
|
$row = mysql_fetch_assoc($r);
|
|
|
|
mysql_free_result($r);
|
|
|
|
return $row;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
} // End getClientData
|
2009-05-06 17:07:05 +02:00
|
|
|
|
|
|
|
function sendResp($status, $apiKey = '') {
|
|
|
|
if ($status == null) {
|
|
|
|
$status = S_BACKEND_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
$a['status'] = $status;
|
|
|
|
$a['t'] = getUTCTimeStamp();
|
|
|
|
$h = sign($a, $apiKey);
|
|
|
|
|
|
|
|
echo "h=" . $h . "\r\n";
|
|
|
|
echo "t=" . ($a['t']) . "\r\n";
|
|
|
|
echo "status=" . ($a['status']) . "\r\n";
|
|
|
|
echo "\r\n";
|
|
|
|
}
|
2008-09-26 05:21:11 +02:00
|
|
|
?>
|