PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/ykval-common.php

http://yubikey-val-server-php.googlecode.com/
PHP | 258 lines | 250 code | 8 blank | 0 comment | 0 complexity | e2a27a2840dc0138c81d2a480316b7d4 MD5 | raw file
Possible License(s): BSD-2-Clause
  1. <?php
  2. require_once('ykval-log.php');
  3. define('S_OK', 'OK');
  4. define('S_BAD_OTP', 'BAD_OTP');
  5. define('S_REPLAYED_OTP', 'REPLAYED_OTP');
  6. define('S_DELAYED_OTP', 'DELAYED_OTP');
  7. define('S_BAD_SIGNATURE', 'BAD_SIGNATURE');
  8. define('S_MISSING_PARAMETER', 'MISSING_PARAMETER');
  9. define('S_NO_SUCH_CLIENT', 'NO_SUCH_CLIENT');
  10. define('S_OPERATION_NOT_ALLOWED', 'OPERATION_NOT_ALLOWED');
  11. define('S_BACKEND_ERROR', 'BACKEND_ERROR');
  12. define('S_NOT_ENOUGH_ANSWERS', 'NOT_ENOUGH_ANSWERS');
  13. define('S_REPLAYED_REQUEST', 'REPLAYED_REQUEST');
  14. define('TS_SEC', 1/8);
  15. define('TS_REL_TOLERANCE', 0.3);
  16. define('TS_ABS_TOLERANCE', 20);
  17. define('TOKEN_LEN', 32);
  18. define('OTP_MAX_LEN', 48); // TOKEN_LEN plus public identity of 0..16
  19. global $ykval_common_log;
  20. $ykval_common_log = new Log('ykval-common');
  21. function logdie ($str)
  22. {
  23. global $ykval_common_log;
  24. $ykval_common_log->log(LOG_INFO, $str);
  25. die($str . "\n");
  26. }
  27. function unescape($s) {
  28. return str_replace('\\', "", $s);
  29. }
  30. function getHttpVal($key, $defaultVal) {
  31. $val = $defaultVal;
  32. if (array_key_exists($key, $_GET)) {
  33. $val = $_GET[$key];
  34. } else if (array_key_exists($key, $_POST)) {
  35. $val = $_POST[$key];
  36. }
  37. $v = unescape(trim($val));
  38. return $v;
  39. }
  40. function debug() {
  41. $str = "";
  42. foreach (func_get_args() as $msg)
  43. {
  44. if (is_array($msg)) {
  45. foreach($msg as $key => $value){
  46. $str .= "$key=$value ";
  47. }
  48. } else {
  49. $str .= $msg . " ";
  50. }
  51. }
  52. global $ykval_common_log;
  53. $ykval_common_log->log(LOG_DEBUG, $str);
  54. }
  55. // Return eg. 2008-11-21T06:11:55Z0711
  56. //
  57. function getUTCTimeStamp() {
  58. date_default_timezone_set('UTC');
  59. $tiny = substr(microtime(false), 2, 3);
  60. return date('Y-m-d\TH:i:s\Z0', time()) . $tiny;
  61. }
  62. # NOTE: When we evolve to using general DB-interface, this functinality
  63. # should be moved there.
  64. function DbTimeToUnix($db_time)
  65. {
  66. $unix=strptime($db_time, '%F %H:%M:%S');
  67. return mktime($unix[tm_hour], $unix[tm_min], $unix[tm_sec], $unix[tm_mon]+1, $unix[tm_mday], $unix[tm_year]+1900);
  68. }
  69. function UnixToDbTime($unix)
  70. {
  71. return date('Y-m-d H:i:s', $unix);
  72. }
  73. // Sign a http query string in the array of key-value pairs
  74. // return b64 encoded hmac hash
  75. function sign($a, $apiKey) {
  76. ksort($a);
  77. $qs = '';
  78. $n = count($a);
  79. $i = 0;
  80. foreach (array_keys($a) as $key) {
  81. $qs .= trim($key).'='.trim($a[$key]);
  82. if (++$i < $n) {
  83. $qs .= '&';
  84. }
  85. }
  86. // the TRUE at the end states we want the raw value, not hexadecimal form
  87. $hmac = hash_hmac('sha1', utf8_encode($qs), $apiKey, true);
  88. $hmac = base64_encode($hmac);
  89. debug('SIGN: ' . $qs . ' H=' . $hmac);
  90. return $hmac;
  91. } // sign an array of query string
  92. function hex2b64 ($hex_str) {
  93. $bin = pack("H*", $hex_str);
  94. return base64_encode($bin);
  95. }
  96. function modhex2b64 ($modhex_str) {
  97. $hex_str = strtr ($modhex_str, "cbdefghijklnrtuv", "0123456789abcdef");
  98. return hex2b64($hex_str);
  99. }
  100. // This function takes a list of URLs. It will return the content of
  101. // the first successfully retrieved URL, whose content matches ^OK.
  102. // The request are sent asynchronously. Some of the URLs can fail
  103. // with unknown host, connection errors, or network timeout, but as
  104. // long as one of the URLs given work, data will be returned. If all
  105. // URLs fail, data from some URL that did not match parameter $match
  106. // (defaults to ^OK) is returned, or if all URLs failed, false.
  107. function retrieveURLasync ($urls, $ans_req=1, $match="^OK", $returl=False) {
  108. $mh = curl_multi_init();
  109. $ch = array();
  110. foreach ($urls as $id => $url) {
  111. $handle = curl_init();
  112. debug("url is: " . $url);
  113. curl_setopt($handle, CURLOPT_URL, $url);
  114. curl_setopt($handle, CURLOPT_USERAGENT, "YK-VAL");
  115. curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
  116. curl_setopt($handle, CURLOPT_FAILONERROR, true);
  117. curl_setopt($handle, CURLOPT_TIMEOUT, 10);
  118. curl_multi_add_handle($mh, $handle);
  119. $ch[$handle] = $handle;
  120. }
  121. $str = false;
  122. $ans_count = 0;
  123. $ans_arr = array();
  124. do {
  125. while (($mrc = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM)
  126. ;
  127. while ($info = curl_multi_info_read($mh)) {
  128. debug ("YK-KSM multi", $info);
  129. if ($info['result'] == CURLE_OK) {
  130. $str = curl_multi_getcontent($info['handle']);
  131. debug($str);
  132. if (preg_match("/".$match."/", $str)) {
  133. $error = curl_error ($info['handle']);
  134. $errno = curl_errno ($info['handle']);
  135. $cinfo = curl_getinfo ($info['handle']);
  136. debug("YK-KSM errno/error: " . $errno . "/" . $error, $cinfo);
  137. $ans_count++;
  138. debug("found entry");
  139. if ($returl) $ans_arr[]="url=" . $cinfo['url'] . "\n" . $str;
  140. else $ans_arr[]=$str;
  141. }
  142. if ($ans_count >= $ans_req) {
  143. foreach ($ch as $h) {
  144. curl_multi_remove_handle ($mh, $h);
  145. curl_close ($h);
  146. }
  147. curl_multi_close ($mh);
  148. if ($ans_count==1) return $ans_arr[0];
  149. else return $ans_arr;
  150. }
  151. curl_multi_remove_handle ($mh, $info['handle']);
  152. curl_close ($info['handle']);
  153. unset ($ch[$info['handle']]);
  154. }
  155. curl_multi_select ($mh);
  156. }
  157. } while($active);
  158. foreach ($ch as $h) {
  159. curl_multi_remove_handle ($mh, $h);
  160. curl_close ($h);
  161. }
  162. curl_multi_close ($mh);
  163. return $str;
  164. }
  165. function retrieveURLsimple ($url, $match="^OK") {
  166. foreach (file($url) as $line) {
  167. if (preg_match("/".$match."/", $line)) {
  168. return $line;
  169. }
  170. }
  171. return false;
  172. }
  173. // $otp: A yubikey OTP
  174. function KSMdecryptOTP($urls) {
  175. $ret = array();
  176. if (!is_array($urls)) {
  177. $response = retrieveURLsimple ($urls);
  178. } elseif (count($urls) == 1) {
  179. $response = retrieveURLsimple ($urls[0]);
  180. } else {
  181. $response = retrieveURLasync ($urls);
  182. }
  183. if ($response) {
  184. debug("YK-KSM response: " . $response);
  185. }
  186. if (sscanf ($response,
  187. "OK counter=%04x low=%04x high=%02x use=%02x",
  188. $ret["session_counter"], $ret["low"], $ret["high"],
  189. $ret["session_use"]) != 4) {
  190. return false;
  191. }
  192. return $ret;
  193. } // End decryptOTP
  194. function sendResp($status, $apiKey = '', $extra = null) {
  195. if ($status == null) {
  196. $status = S_BACKEND_ERROR;
  197. }
  198. $a['status'] = $status;
  199. $a['t'] = getUTCTimeStamp();
  200. if ($extra){
  201. foreach ($extra as $param => $value) $a[$param] = $value;
  202. }
  203. $h = sign($a, $apiKey);
  204. $str = "h=" . $h . "\r\n";
  205. $str .= "t=" . ($a['t']) . "\r\n";
  206. if ($extra){
  207. foreach ($extra as $param => $value) {
  208. $str .= $param . "=" . $value . "\r\n";
  209. }
  210. }
  211. $str .= "status=" . ($a['status']) . "\r\n";
  212. $str .= "\r\n";
  213. global $ykval_common_log;
  214. $ykval_common_log->log(LOG_INFO, "Response: " . $str .
  215. " (at " . date("c") . " " . microtime() . ")");
  216. echo $str;
  217. }
  218. ?>