PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/phpMyAdmin/libraries/auth/swekey/swekey.php

https://bitbucket.org/izubizarreta/https-bitbucket.org-bityvip
PHP | 495 lines | 319 code | 44 blank | 132 comment | 36 complexity | c8a031541cabf41ed7157814932e26b1 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.0, JSON, GPL-2.0, BSD-3-Clause, LGPL-2.1, MIT
  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. $sess = curl_init($url);
  169. if (substr($url, 0, 8) == "https://") {
  170. global $gSwekeyCA;
  171. if (! empty($gSwekeyCA)) {
  172. if (file_exists($gSwekeyCA)) {
  173. if (! curl_setopt($sess, CURLOPT_CAINFO, $gSwekeyCA)) {
  174. error_log("SWEKEY_ERROR:Could not set CA file : ".curl_error($sess));
  175. } else {
  176. $caFileOk = true;
  177. }
  178. } else {
  179. error_log("SWEKEY_ERROR:Could not find CA file $gSwekeyCA getting $url");
  180. }
  181. }
  182. curl_setopt($sess, CURLOPT_SSL_VERIFYHOST, '2');
  183. curl_setopt($sess, CURLOPT_SSL_VERIFYPEER, '2');
  184. curl_setopt($sess, CURLOPT_CONNECTTIMEOUT, '20');
  185. curl_setopt($sess, CURLOPT_TIMEOUT, '20');
  186. } else {
  187. curl_setopt($sess, CURLOPT_CONNECTTIMEOUT, '3');
  188. curl_setopt($sess, CURLOPT_TIMEOUT, '5');
  189. }
  190. curl_setopt($sess, CURLOPT_RETURNTRANSFER, '1');
  191. $res=curl_exec($sess);
  192. $response_code = curl_getinfo($sess, CURLINFO_HTTP_CODE);
  193. $curlerr = curl_error($sess);
  194. curl_close($sess);
  195. if ($response_code == 200) {
  196. $gSwekeyLastResult = $res;
  197. return $res;
  198. }
  199. if (! empty($response_code)) {
  200. $gSwekeyLastError = $response_code;
  201. error_log("SWEKEY_ERROR:Error $gSwekeyLastError ($curlerr) getting $url");
  202. return "";
  203. }
  204. $response_code = 408; // Request Timeout
  205. $gSwekeyLastError = $response_code;
  206. error_log("SWEKEY_ERROR:Error $curlerr getting $url");
  207. return "";
  208. }
  209. // use pecl_http if available
  210. if (class_exists('HttpRequest')) {
  211. // retry if one of the server is down
  212. for ($num=1; $num <= 3; $num++ ) {
  213. $r = new HttpRequest($url);
  214. $options = array('timeout' => '3');
  215. if (substr($url, 0, 6) == "https:") {
  216. $sslOptions = array();
  217. $sslOptions['verifypeer'] = true;
  218. $sslOptions['verifyhost'] = true;
  219. $capath = __FILE__;
  220. $name = strrchr($capath, '/');
  221. // windows
  222. if (empty($name)) {
  223. $name = strrchr($capath, '\\');
  224. }
  225. $capath = substr($capath, 0, strlen($capath) - strlen($name) + 1).'musbe-ca.crt';
  226. if (! empty($gSwekeyCA)) {
  227. $sslOptions['cainfo'] = $gSwekeyCA;
  228. }
  229. $options['ssl'] = $sslOptions;
  230. }
  231. $r->setOptions($options);
  232. // try
  233. {
  234. $reply = $r->send();
  235. $res = $reply->getBody();
  236. $info = $r->getResponseInfo();
  237. $response_code = $info['response_code'];
  238. if ($response_code != 200)
  239. {
  240. $gSwekeyLastError = $response_code;
  241. error_log("SWEKEY_ERROR:Error ".$gSwekeyLastError." getting ".$url);
  242. return "";
  243. }
  244. $gSwekeyLastResult = $res;
  245. return $res;
  246. }
  247. // catch (HttpException $e)
  248. // {
  249. // error_log("SWEKEY_WARNING:HttpException ".$e." getting ".$url);
  250. // }
  251. }
  252. $response_code = 408; // Request Timeout
  253. $gSwekeyLastError = $response_code;
  254. error_log("SWEKEY_ERROR:Error ".$gSwekeyLastError." getting ".$url);
  255. return "";
  256. }
  257. global $http_response_header;
  258. $res = @file_get_contents($url);
  259. $response_code = substr($http_response_header[0], 9, 3); //HTTP/1.0
  260. if ($response_code == 200) {
  261. $gSwekeyLastResult = $res;
  262. return $res;
  263. }
  264. $gSwekeyLastError = $response_code;
  265. error_log("SWEKEY_ERROR:Error ".$response_code." getting ".$url);
  266. return "";
  267. }
  268. /**
  269. * Get a Random Token from a Token Server
  270. * The RT is a 64 vhars hexadecimal value
  271. * You should better use Swekey_GetFastRndToken() for performance
  272. * @access public
  273. */
  274. function Swekey_GetRndToken()
  275. {
  276. global $gSwekeyRndTokenServer;
  277. return Swekey_HttpGet($gSwekeyRndTokenServer.'/FULL-RND-TOKEN', $response_code);
  278. }
  279. /**
  280. * Get a Half Random Token from a Token Server
  281. * The RT is a 64 vhars hexadecimal value
  282. * Use this value if you want to make your own Swekey_GetFastRndToken()
  283. * @access public
  284. */
  285. function Swekey_GetHalfRndToken()
  286. {
  287. global $gSwekeyRndTokenServer;
  288. return Swekey_HttpGet($gSwekeyRndTokenServer.'/HALF-RND-TOKEN', $response_code);
  289. }
  290. /**
  291. * Get a Half Random Token
  292. * The RT is a 64 vhars hexadecimal value
  293. * This function get a new random token and reuse it.
  294. * Token are refetched from the server only once every 30 seconds.
  295. * You should always use this function to get half random token.
  296. * @access public
  297. */
  298. function Swekey_GetFastHalfRndToken()
  299. {
  300. global $gSwekeyTokenCacheEnabled;
  301. $res = "";
  302. $cachefile = "";
  303. // We check if we have a valid RT is the session
  304. if (isset($_SESSION['rnd-token-date'])) {
  305. if (time() - $_SESSION['rnd-token-date'] < 30) {
  306. $res = $_SESSION['rnd-token'];
  307. }
  308. }
  309. // If not we try to get it from a temp file (PHP >= 5.2.1 only)
  310. if (strlen($res) != 32 && $gSwekeyTokenCacheEnabled) {
  311. if (function_exists('sys_get_temp_dir')) {
  312. $tempdir = sys_get_temp_dir();
  313. $cachefile = $tempdir."/swekey-rnd-token-".get_current_user();
  314. $modif = filemtime($cachefile);
  315. if ($modif != false) {
  316. if (time() - $modif < 30) {
  317. $res = @file_get_contents($cachefile);
  318. if (strlen($res) != 32) {
  319. $res = "";
  320. } else {
  321. $_SESSION['rnd-token'] = $res;
  322. $_SESSION['rnd-token-date'] = $modif;
  323. }
  324. }
  325. }
  326. }
  327. }
  328. // If we don't have a valid RT here we have to get it from the server
  329. if (strlen($res) != 32) {
  330. $res = substr(Swekey_GetHalfRndToken(), 0, 32);
  331. $_SESSION['rnd-token'] = $res;
  332. $_SESSION['rnd-token-date'] = time();
  333. if (! empty($cachefile)) {
  334. // we unlink the file so no possible tempfile race attack
  335. unlink($cachefile);
  336. $file = fopen($cachefile, "x");
  337. if ($file != false) {
  338. @fwrite($file, $res);
  339. @fclose($file);
  340. }
  341. }
  342. }
  343. return $res."00000000000000000000000000000000";
  344. }
  345. /**
  346. * Get a Random Token
  347. * The RT is a 64 vhars hexadecimal value
  348. * This function generates a unique random token for each call but call the
  349. * server only once every 30 seconds.
  350. * You should always use this function to get random token.
  351. * @access public
  352. */
  353. function Swekey_GetFastRndToken()
  354. {
  355. $res = Swekey_GetFastHalfRndToken();
  356. if (strlen($res) == 64)
  357. return substr($res, 0, 32).strtoupper(md5("Musbe Authentication Key" + mt_rand() + date(DATE_ATOM)));
  358. return "";
  359. }
  360. /**
  361. * Checks that an OTP generated by a Swekey is valid
  362. *
  363. * @param id The id of the swekey
  364. * @param rt The random token used to generate the otp
  365. * @param otp The otp generated by the swekey
  366. * @return true or false
  367. * @access public
  368. */
  369. function Swekey_CheckOtp($id, $rt, $otp)
  370. {
  371. global $gSwekeyCheckServer;
  372. $res = Swekey_HttpGet($gSwekeyCheckServer.'/CHECK-OTP/'.$id.'/'.$rt.'/'.$otp, $response_code);
  373. return $response_code == 200 && $res == "OK";
  374. }
  375. /**
  376. * Values that are associated with a key.
  377. * The following values can be returned by the Swekey_GetStatus() function
  378. */
  379. define ("SWEKEY_STATUS_OK", 0);
  380. define ("SWEKEY_STATUS_NOT_FOUND", 1); // The key does not exist in the db
  381. define ("SWEKEY_STATUS_INACTIVE", 2); // The key has never been activated
  382. define ("SWEKEY_STATUS_LOST", 3); // The user has lost his key
  383. define ("SWEKEY_STATUS_STOLEN", 4); // The key was stolen
  384. define ("SWEKEY_STATUS_FEE_DUE", 5); // The annual fee was not paid
  385. define ("SWEKEY_STATUS_OBSOLETE", 6); // The hardware is no longer supported
  386. define ("SWEKEY_STATUS_UNKOWN", 201); // We could not connect to the authentication server
  387. /**
  388. * Values that are associated with a key.
  389. * The Javascript Api can also return the following values
  390. */
  391. define ("SWEKEY_STATUS_REPLACED", 100); // This key has been replaced by a backup key
  392. define ("SWEKEY_STATUS_BACKUP_KEY", 101); // This key is a backup key that is not activated yet
  393. define ("SWEKEY_STATUS_NOTPLUGGED", 200); // This key is not plugged in the computer
  394. /**
  395. * Return the text corresponding to the integer status of a key
  396. *
  397. * @param status The status
  398. * @return The text corresponding to the status
  399. * @access public
  400. */
  401. function Swekey_GetStatusStr($status)
  402. {
  403. switch($status)
  404. {
  405. case SWEKEY_STATUS_OK : return 'OK';
  406. case SWEKEY_STATUS_NOT_FOUND : return 'Key does not exist in the db';
  407. case SWEKEY_STATUS_INACTIVE : return 'Key not activated';
  408. case SWEKEY_STATUS_LOST : return 'Key was lost';
  409. case SWEKEY_STATUS_STOLEN : return 'Key was stolen';
  410. case SWEKEY_STATUS_FEE_DUE : return 'The annual fee was not paid';
  411. case SWEKEY_STATUS_OBSOLETE : return 'Key no longer supported';
  412. case SWEKEY_STATUS_REPLACED : return 'This key has been replaced by a backup key';
  413. case SWEKEY_STATUS_BACKUP_KEY : return 'This key is a backup key that is not activated yet';
  414. case SWEKEY_STATUS_NOTPLUGGED : return 'This key is not plugged in the computer';
  415. case SWEKEY_STATUS_UNKOWN : return 'Unknow Status, could not connect to the authentication server';
  416. }
  417. return 'unknown status '.$status;
  418. }
  419. /**
  420. * If your web site requires a key to login you should check that the key
  421. * is still valid (has not been lost or stolen) before requiring it.
  422. * A key can be authenticated only if its status is SWEKEY_STATUS_OK
  423. * @param id The id of the swekey
  424. * @return The status of the swekey
  425. * @access public
  426. */
  427. function Swekey_GetStatus($id)
  428. {
  429. global $gSwekeyStatusServer;
  430. $res = Swekey_HttpGet($gSwekeyStatusServer.'/GET-STATUS/'.$id, $response_code);
  431. if ($response_code == 200)
  432. return intval($res);
  433. return SWEKEY_STATUS_UNKOWN;
  434. }
  435. ?>