PageRenderTime 46ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/phpmyadmin/libraries/auth/swekey/swekey.php

https://github.com/steph/PortFolio
PHP | 507 lines | 332 code | 44 blank | 131 comment | 33 complexity | 18721744371c4447cb6f6ccdaec6ec5c MD5 | raw file
  1. <?php
  2. /**
  3. * Library that provides common functions that are used to help integrating Swekey Authentication in a PHP web site
  4. * Version 1.0
  5. *
  6. * History:
  7. * 1.2 Use curl (widely installed) to query the server
  8. * Fixed a possible tempfile race attack
  9. * Random token cache can now be disabled
  10. * 1.1 Added Swekey_HttpGet function that support faulty servers
  11. * Support for custom servers
  12. * 1.0 First release
  13. *
  14. * @package Swekey
  15. */
  16. /**
  17. * Errors codes
  18. */
  19. define ("SWEKEY_ERR_INVALID_DEV_STATUS",901); // The satus of the device is not SWEKEY_STATUS_OK
  20. define ("SWEKEY_ERR_INTERNAL",902); // Should never occurd
  21. define ("SWEKEY_ERR_OUTDATED_RND_TOKEN",910); // You random token is too old
  22. define ("SWEKEY_ERR_INVALID_OTP",911); // The otp was not correct
  23. /**
  24. * Those errors are considered as an attack and your site will be blacklisted during one minute
  25. * if you receive one of those errors
  26. */
  27. define ("SWEKEY_ERR_BADLY_ENCODED_REQUEST",920);
  28. define ("SWEKEY_ERR_INVALID_RND_TOKEN",921);
  29. define ("SWEKEY_ERR_DEV_NOT_FOUND",922);
  30. /**
  31. * Default values for configuration.
  32. */
  33. define ('SWEKEY_DEFAULT_CHECK_SERVER', 'https://auth-check.musbe.net');
  34. define ('SWEKEY_DEFAULT_RND_SERVER', 'https://auth-rnd-gen.musbe.net');
  35. define ('SWEKEY_DEFAULT_STATUS_SERVER', 'https://auth-status.musbe.net');
  36. /**
  37. * The last error of an operation is alway put in this global var
  38. */
  39. global $gSwekeyLastError;
  40. $gSwekeyLastError = 0;
  41. global $gSwekeyLastResult;
  42. $gSwekeyLastResult = "<not set>";
  43. /**
  44. * Servers addresses
  45. * Use the Swekey_SetXxxServer($server) functions to set them
  46. */
  47. global $gSwekeyCheckServer;
  48. if (! isset($gSwekeyCheckServer))
  49. $gSwekeyCheckServer = SWEKEY_DEFAULT_CHECK_SERVER;
  50. global $gSwekeyRndTokenServer;
  51. if (! isset($gSwekeyRndTokenServer))
  52. $gSwekeyRndTokenServer = SWEKEY_DEFAULT_RND_SERVER;
  53. global $gSwekeyStatusServer;
  54. if (! isset($gSwekeyStatusServer))
  55. $gSwekeyStatusServer = SWEKEY_DEFAULT_STATUS_SERVER;
  56. global $gSwekeyCA;
  57. global $gSwekeyTokenCacheEnabled;
  58. if (! isset($gSwekeyTokenCacheEnabled))
  59. $gSwekeyTokenCacheEnabled = true;
  60. /**
  61. * Change the address of the Check server.
  62. * If $server is empty the default value 'http://auth-check.musbe.net' will be used
  63. *
  64. * @param server The protocol and hostname to use
  65. * @access public
  66. */
  67. function Swekey_SetCheckServer($server)
  68. {
  69. global $gSwekeyCheckServer;
  70. if (empty($server))
  71. $gSwekeyCheckServer = SWEKEY_DEFAULT_CHECK_SERVER;
  72. else
  73. $gSwekeyCheckServer = $server;
  74. }
  75. /**
  76. * Change the address of the Random Token Generator server.
  77. * If $server is empty the default value 'http://auth-rnd-gen.musbe.net' will be used
  78. *
  79. * @param server The protocol and hostname to use
  80. * @access public
  81. */
  82. function Swekey_SetRndTokenServer($server)
  83. {
  84. global $gSwekeyRndTokenServer;
  85. if (empty($server))
  86. $gSwekeyRndTokenServer = SWEKEY_DEFAULT_RND_SERVER;
  87. else
  88. $gSwekeyRndTokenServer = $server;
  89. }
  90. /**
  91. * Change the address of the Satus server.
  92. * If $server is empty the default value 'http://auth-status.musbe.net' will be used
  93. *
  94. * @param server The protocol and hostname to use
  95. * @access public
  96. */
  97. function Swekey_SetStatusServer($server)
  98. {
  99. global $gSwekeyStatusServer;
  100. if (empty($server))
  101. $gSwekeyStatusServer = SWEKEY_DEFAULT_STATUS_SERVER;
  102. else
  103. $gSwekeyStatusServer = $server;
  104. }
  105. /**
  106. * Change the certificat file in case of the the severs use https instead of http
  107. *
  108. * @param cafile The path of the crt file to use
  109. * @access public
  110. */
  111. function Swekey_SetCAFile($cafile)
  112. {
  113. global $gSwekeyCA;
  114. $gSwekeyCA = $cafile;
  115. }
  116. /**
  117. * Enable or disable the random token caching
  118. * Because everybody has full access to the cache file, it can be a DOS vulnerability
  119. * So disable it if you are running in a non secure enviromnement
  120. *
  121. * @param $enable
  122. * @access public
  123. */
  124. function Swekey_EnableTokenCache($enable)
  125. {
  126. global $gSwekeyTokenCacheEnabled;
  127. $gSwekeyTokenCacheEnabled = ! empty($enable);
  128. }
  129. /**
  130. * Return the last error.
  131. *
  132. * @return The Last Error
  133. * @access public
  134. */
  135. function Swekey_GetLastError()
  136. {
  137. global $gSwekeyLastError;
  138. return $gSwekeyLastError;
  139. }
  140. /**
  141. * Return the last result.
  142. *
  143. * @return The Last Error
  144. * @access public
  145. */
  146. function Swekey_GetLastResult()
  147. {
  148. global $gSwekeyLastResult;
  149. return $gSwekeyLastResult;
  150. }
  151. /**
  152. * Send a synchronous request to the server.
  153. * This function manages timeout then will not block if one of the server is down
  154. *
  155. * @param url The url to get
  156. * @param response_code The response code
  157. * @return The body of the response or "" in case of error
  158. * @access private
  159. */
  160. function Swekey_HttpGet($url, &$response_code)
  161. {
  162. global $gSwekeyLastError;
  163. $gSwekeyLastError = 0;
  164. global $gSwekeyLastResult;
  165. $gSwekeyLastResult = "<not set>";
  166. // use curl if available
  167. if (function_exists('curl_init'))
  168. {
  169. $sess = curl_init($url);
  170. if (substr($url, 0, 8) == "https://")
  171. {
  172. global $gSwekeyCA;
  173. if (! empty($gSwekeyCA))
  174. {
  175. if (file_exists($gSwekeyCA))
  176. {
  177. if (! curl_setopt($sess, CURLOPT_CAINFO, $gSwekeyCA))
  178. error_log("SWEKEY_ERROR:Could not set CA file : ".curl_error($sess));
  179. else
  180. $caFileOk = true;
  181. }
  182. else
  183. error_log("SWEKEY_ERROR:Could not find CA file $gSwekeyCA getting $url");
  184. }
  185. curl_setopt($sess, CURLOPT_SSL_VERIFYHOST, '2');
  186. curl_setopt($sess, CURLOPT_SSL_VERIFYPEER, '2');
  187. curl_setopt($sess, CURLOPT_CONNECTTIMEOUT, '20');
  188. curl_setopt($sess, CURLOPT_TIMEOUT, '20');
  189. }
  190. else
  191. {
  192. curl_setopt($sess, CURLOPT_CONNECTTIMEOUT, '3');
  193. curl_setopt($sess, CURLOPT_TIMEOUT, '5');
  194. }
  195. curl_setopt($sess, CURLOPT_RETURNTRANSFER, '1');
  196. $res=curl_exec($sess);
  197. $response_code = curl_getinfo($sess, CURLINFO_HTTP_CODE);
  198. $curlerr = curl_error($sess);
  199. curl_close($sess);
  200. if ($response_code == 200)
  201. {
  202. $gSwekeyLastResult = $res;
  203. return $res;
  204. }
  205. if (! empty($response_code))
  206. {
  207. $gSwekeyLastError = $response_code;
  208. error_log("SWEKEY_ERROR:Error $gSwekeyLastError ($curlerr) getting $url");
  209. return "";
  210. }
  211. $response_code = 408; // Request Timeout
  212. $gSwekeyLastError = $response_code;
  213. error_log("SWEKEY_ERROR:Error $curlerr getting $url");
  214. return "";
  215. }
  216. // use pecl_http if available
  217. if (class_exists('HttpRequest'))
  218. {
  219. // retry if one of the server is down
  220. for ($num=1; $num <= 3; $num++ )
  221. {
  222. $r = new HttpRequest($url);
  223. $options = array('timeout' => '3');
  224. if (substr($url,0, 6) == "https:")
  225. {
  226. $sslOptions = array();
  227. $sslOptions['verifypeer'] = true;
  228. $sslOptions['verifyhost'] = true;
  229. $capath = __FILE__;
  230. $name = strrchr($capath, '/');
  231. if (empty($name)) // windows
  232. $name = strrchr($capath, '\\');
  233. $capath = substr($capath, 0, strlen($capath) - strlen($name) + 1).'musbe-ca.crt';
  234. if (! empty($gSwekeyCA))
  235. $sslOptions['cainfo'] = $gSwekeyCA;
  236. $options['ssl'] = $sslOptions;
  237. }
  238. $r->setOptions($options);
  239. // try
  240. {
  241. $reply = $r->send();
  242. $res = $reply->getBody();
  243. $info = $r->getResponseInfo();
  244. $response_code = $info['response_code'];
  245. if ($response_code != 200)
  246. {
  247. $gSwekeyLastError = $response_code;
  248. error_log("SWEKEY_ERROR:Error ".$gSwekeyLastError." getting ".$url);
  249. return "";
  250. }
  251. $gSwekeyLastResult = $res;
  252. return $res;
  253. }
  254. // catch (HttpException $e)
  255. // {
  256. // error_log("SWEKEY_WARNING:HttpException ".$e." getting ".$url);
  257. // }
  258. }
  259. $response_code = 408; // Request Timeout
  260. $gSwekeyLastError = $response_code;
  261. error_log("SWEKEY_ERROR:Error ".$gSwekeyLastError." getting ".$url);
  262. return "";
  263. }
  264. global $http_response_header;
  265. $res = @file_get_contents($url);
  266. $response_code = substr($http_response_header[0], 9, 3); //HTTP/1.0
  267. if ($response_code == 200)
  268. {
  269. $gSwekeyLastResult = $res;
  270. return $res;
  271. }
  272. $gSwekeyLastError = $response_code;
  273. error_log("SWEKEY_ERROR:Error ".$response_code." getting ".$url);
  274. return "";
  275. }
  276. /**
  277. * Get a Random Token from a Token Server
  278. * The RT is a 64 vhars hexadecimal value
  279. * You should better use Swekey_GetFastRndToken() for performance
  280. * @access public
  281. */
  282. function Swekey_GetRndToken()
  283. {
  284. global $gSwekeyRndTokenServer;
  285. return Swekey_HttpGet($gSwekeyRndTokenServer.'/FULL-RND-TOKEN', $response_code);
  286. }
  287. /**
  288. * Get a Half Random Token from a Token Server
  289. * The RT is a 64 vhars hexadecimal value
  290. * Use this value if you want to make your own Swekey_GetFastRndToken()
  291. * @access public
  292. */
  293. function Swekey_GetHalfRndToken()
  294. {
  295. global $gSwekeyRndTokenServer;
  296. return Swekey_HttpGet($gSwekeyRndTokenServer.'/HALF-RND-TOKEN', $response_code);
  297. }
  298. /**
  299. * Get a Half Random Token
  300. * The RT is a 64 vhars hexadecimal value
  301. * This function get a new random token and reuse it.
  302. * Token are refetched from the server only once every 30 seconds.
  303. * You should always use this function to get half random token.
  304. * @access public
  305. */
  306. function Swekey_GetFastHalfRndToken()
  307. {
  308. global $gSwekeyTokenCacheEnabled;
  309. $res = "";
  310. $cachefile = "";
  311. // We check if we have a valid RT is the session
  312. if (isset($_SESSION['rnd-token-date']))
  313. if (time() - $_SESSION['rnd-token-date'] < 30)
  314. $res = $_SESSION['rnd-token'];
  315. // If not we try to get it from a temp file (PHP >= 5.2.1 only)
  316. if (strlen($res) != 32 && $gSwekeyTokenCacheEnabled)
  317. {
  318. if (function_exists('sys_get_temp_dir'))
  319. {
  320. $tempdir = sys_get_temp_dir();
  321. $cachefile = $tempdir."/swekey-rnd-token-".get_current_user();
  322. $modif = filemtime($cachefile);
  323. if ($modif != false)
  324. if (time() - $modif < 30)
  325. {
  326. $res = @file_get_contents($cachefile);
  327. if (strlen($res) != 32)
  328. $res = "";
  329. else
  330. {
  331. $_SESSION['rnd-token'] = $res;
  332. $_SESSION['rnd-token-date'] = $modif;
  333. }
  334. }
  335. }
  336. }
  337. // If we don't have a valid RT here we have to get it from the server
  338. if (strlen($res) != 32)
  339. {
  340. $res = substr(Swekey_GetHalfRndToken(), 0, 32);
  341. $_SESSION['rnd-token'] = $res;
  342. $_SESSION['rnd-token-date'] = time();
  343. if (! empty($cachefile))
  344. {
  345. // we unlink the file so no possible tempfile race attack
  346. unlink($cachefile);
  347. $file = fopen($cachefile , "x");
  348. if ($file != FALSE)
  349. {
  350. @fwrite($file, $res);
  351. @fclose($file);
  352. }
  353. }
  354. }
  355. return $res."00000000000000000000000000000000";
  356. }
  357. /**
  358. * Get a Random Token
  359. * The RT is a 64 vhars hexadecimal value
  360. * This function generates a unique random token for each call but call the
  361. * server only once every 30 seconds.
  362. * You should always use this function to get random token.
  363. * @access public
  364. */
  365. function Swekey_GetFastRndToken()
  366. {
  367. $res = Swekey_GetFastHalfRndToken();
  368. if (strlen($res) == 64)
  369. return substr($res, 0, 32).strtoupper(md5("Musbe Authentication Key" + mt_rand() + date(DATE_ATOM)));
  370. return "";
  371. }
  372. /**
  373. * Checks that an OTP generated by a Swekey is valid
  374. *
  375. * @param id The id of the swekey
  376. * @param rt The random token used to generate the otp
  377. * @param otp The otp generated by the swekey
  378. * @return true or false
  379. * @access public
  380. */
  381. function Swekey_CheckOtp($id, $rt, $otp)
  382. {
  383. global $gSwekeyCheckServer;
  384. $res = Swekey_HttpGet($gSwekeyCheckServer.'/CHECK-OTP/'.$id.'/'.$rt.'/'.$otp, $response_code);
  385. return $response_code == 200 && $res == "OK";
  386. }
  387. /**
  388. * Values that are associated with a key.
  389. * The following values can be returned by the Swekey_GetStatus() function
  390. */
  391. define ("SWEKEY_STATUS_OK",0);
  392. define ("SWEKEY_STATUS_NOT_FOUND",1); // The key does not exist in the db
  393. define ("SWEKEY_STATUS_INACTIVE",2); // The key has never been activated
  394. define ("SWEKEY_STATUS_LOST",3); // The user has lost his key
  395. define ("SWEKEY_STATUS_STOLEN",4); // The key was stolen
  396. define ("SWEKEY_STATUS_FEE_DUE",5); // The annual fee was not paid
  397. define ("SWEKEY_STATUS_OBSOLETE",6); // The hardware is no longer supported
  398. define ("SWEKEY_STATUS_UNKOWN",201); // We could not connect to the authentication server
  399. /**
  400. * Values that are associated with a key.
  401. * The Javascript Api can also return the following values
  402. */
  403. define ("SWEKEY_STATUS_REPLACED",100); // This key has been replaced by a backup key
  404. define ("SWEKEY_STATUS_BACKUP_KEY",101); // This key is a backup key that is not activated yet
  405. define ("SWEKEY_STATUS_NOTPLUGGED",200); // This key is not plugged in the computer
  406. /**
  407. * Return the text corresponding to the integer status of a key
  408. *
  409. * @param status The status
  410. * @return The text corresponding to the status
  411. * @access public
  412. */
  413. function Swekey_GetStatusStr($status)
  414. {
  415. switch($status)
  416. {
  417. case SWEKEY_STATUS_OK : return 'OK';
  418. case SWEKEY_STATUS_NOT_FOUND : return 'Key does not exist in the db';
  419. case SWEKEY_STATUS_INACTIVE : return 'Key not activated';
  420. case SWEKEY_STATUS_LOST : return 'Key was lost';
  421. case SWEKEY_STATUS_STOLEN : return 'Key was stolen';
  422. case SWEKEY_STATUS_FEE_DUE : return 'The annual fee was not paid';
  423. case SWEKEY_STATUS_OBSOLETE : return 'Key no longer supported';
  424. case SWEKEY_STATUS_REPLACED : return 'This key has been replaced by a backup key';
  425. case SWEKEY_STATUS_BACKUP_KEY : return 'This key is a backup key that is not activated yet';
  426. case SWEKEY_STATUS_NOTPLUGGED : return 'This key is not plugged in the computer';
  427. case SWEKEY_STATUS_UNKOWN : return 'Unknow Status, could not connect to the authentication server';
  428. }
  429. return 'unknown status '.$status;
  430. }
  431. /**
  432. * If your web site requires a key to login you should check that the key
  433. * is still valid (has not been lost or stolen) before requiring it.
  434. * A key can be authenticated only if its status is SWEKEY_STATUS_OK
  435. * @param id The id of the swekey
  436. * @return The status of the swekey
  437. * @access public
  438. */
  439. function Swekey_GetStatus($id)
  440. {
  441. global $gSwekeyStatusServer;
  442. $res = Swekey_HttpGet($gSwekeyStatusServer.'/GET-STATUS/'.$id, $response_code);
  443. if ($response_code == 200)
  444. return intval($res);
  445. return SWEKEY_STATUS_UNKOWN;
  446. }
  447. ?>