log(LOG_EMERG, $str); die($str . "\n"); } function unescape($s) { return str_replace('\\', "", $s); } function getHttpVal($key, $defaultVal) { $val = $defaultVal; if (array_key_exists($key, $_GET)) { $val = $_GET[$key]; } else if (array_key_exists($key, $_POST)) { $val = $_POST[$key]; } $v = unescape(trim($val)); return $v; } function query($conn, $q) { debug('SQL query: ' . $q); $result = mysql_query($q, $conn); if (!$result) { debug("SQL query error: " . mysql_error()); return; } return $result; } function mysql_quote($value) { return "'" . mysql_real_escape_string($value) . "'"; } function debug() { $str = ""; foreach (func_get_args() as $msg) { if (is_array($msg)) { foreach($msg as $key => $value){ $str .= "$key=$value "; } } else { $str .= $msg . " "; } } global $ykval_common_log; $ykval_common_log->log(LOG_DEBUG, $str); } // Return eg. 2008-11-21T06:11:55Z0711 // function getUTCTimeStamp() { date_default_timezone_set('UTC'); $tiny = substr(microtime(false), 2, 3); return date('Y-m-d\TH:i:s\Z0', time()) . $tiny; } # NOTE: When we evolve to using general DB-interface, this functinality # should be moved there. function DbTimeToUnix($db_time) { $unix=strptime($db_time, '%F %H:%M:%S'); return mktime($unix[tm_hour], $unix[tm_min], $unix[tm_sec], $unix[tm_mon]+1, $unix[tm_mday], $unix[tm_year]+1900); } function UnixToDbTime($unix) { return date('Y-m-d H:i:s', $unix); } // Sign a http query string in the array of key-value pairs // return b64 encoded hmac hash function sign($a, $apiKey) { ksort($a); $qs = ''; $n = count($a); $i = 0; foreach (array_keys($a) as $key) { $qs .= trim($key).'='.trim($a[$key]); 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); debug('SIGN: ' . $qs . ' H=' . $hmac); return $hmac; } // sign an array of query string function hex2b64 ($hex_str) { $bin = pack("H*", $hex_str); return base64_encode($bin); } function modhex2b64 ($modhex_str) { $hex_str = strtr ($modhex_str, "cbdefghijklnrtuv", "0123456789abcdef"); return hex2b64($hex_str); } // This function takes a list of URLs. It will return the content of // the first successfully retrieved URL, whose content matches ^OK. // The request are sent asynchronously. Some of the URLs can fail // with unknown host, connection errors, or network timeout, but as // long as one of the URLs given work, data will be returned. If all // URLs fail, data from some URL that did not match parameter $match // (defaults to ^OK) is returned, or if all URLs failed, false. function retrieveURLasync ($urls, $ans_req=1, $match="^OK", $returl=False) { $mh = curl_multi_init(); $ch = array(); foreach ($urls as $id => $url) { $handle = curl_init(); debug("url is: " . $url); curl_setopt($handle, CURLOPT_URL, $url); curl_setopt($handle, CURLOPT_USERAGENT, "YK-VAL"); curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); curl_setopt($handle, CURLOPT_FAILONERROR, true); curl_setopt($handle, CURLOPT_TIMEOUT, 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("/".$match."/", $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"); 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); 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']]); } 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); } if (sscanf ($response, "OK counter=%04x low=%04x high=%02x use=%02x", $ret["session_counter"], $ret["low"], $ret["high"], $ret["session_use"]) != 4) { return false; } return $ret; } // End decryptOTP // $devId: The first 12 chars from the OTP function getAuthData($conn, $devId) { $publicName = modhex2b64($devId); $stmt = 'SELECT id, active, counter, sessionUse, low, high, accessed '. 'FROM yubikeys '. 'WHERE publicName='.mysql_quote($publicName); $r = query($conn, $stmt); if (mysql_num_rows($r) > 0) { $row = mysql_fetch_assoc($r); mysql_free_result($r); return $row; } return null; } // End getAuthData function addNewKey($conn, $devId) { $publicName = modhex2b64($devId); $stmt = 'INSERT INTO yubikeys (active, created, publicName, counter) '. 'VALUES (true, NOW(), ' . mysql_quote($publicName) . ', 0)'; $r = query($conn, $stmt); } // $clientId: The decimal client identity function getClientData($conn, $clientId) { $stmt = 'SELECT id, secret '. 'FROM clients '. 'WHERE active AND id='.mysql_quote($clientId); $r = query($conn, $stmt); if (mysql_num_rows($r) > 0) { $row = mysql_fetch_assoc($r); mysql_free_result($r); return $row; } return null; } // End getClientData function sendResp($status, $apiKey = '', $extra = null) { if ($status == null) { $status = S_BACKEND_ERROR; } $a['status'] = $status; $a['t'] = getUTCTimeStamp(); if ($extra){ foreach ($extra as $param => $value) $a[$param] = $value; } $h = sign($a, $apiKey); echo "h=" . $h . "\r\n"; echo "t=" . ($a['t']) . "\r\n"; if ($extra){ foreach ($extra as $param => $value) { echo $param . "=" . $value . "\r\n"; } } echo "status=" . ($a['status']) . "\r\n"; echo "\r\n"; } ?>