PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/phpmyid.php

https://github.com/albertz/smf-openid-server
PHP | 1817 lines | 1029 code | 366 blank | 422 comment | 250 complexity | 138f25b2cb77b0bedd791c368ebf202b MD5 | raw file
  1. <?php
  2. // PLEASE DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING!
  3. /**
  4. * phpMyID - A standalone, single user, OpenID Identity Provider
  5. *
  6. * @package phpMyID
  7. * @author CJ Niemira <siege (at) siege (dot) org>
  8. * @copyright 2006-2008
  9. * @license http://www.gnu.org/licenses/gpl.html GNU Public License
  10. * @url http://siege.org/projects/phpMyID
  11. * @version 0.9
  12. */
  13. $wgSMFVersion = "2.0";
  14. $wgSMFLogin = true;
  15. $wgSMFPath = "../forum";
  16. $wgSMFAdminGroupName = array('Wiki Admin', 'Global Moderator', 'Administrator');
  17. require_once("./Auth_SMF.php");
  18. $wgAuth = new Auth_SMF();
  19. /**
  20. * Set a constant to indicate that phpMyID is running
  21. */
  22. define('PHPMYID_STARTED', true);
  23. /**
  24. * List the known types and modes
  25. * @name $known
  26. * @global array $GLOBALS['known']
  27. */
  28. $GLOBALS['known'] = array(
  29. 'assoc_types' => array('HMAC-SHA1'),
  30. 'openid_modes' => array('accept',
  31. 'associate',
  32. 'authorize',
  33. 'cancel',
  34. 'checkid_immediate',
  35. 'checkid_setup',
  36. 'check_authentication',
  37. 'error',
  38. 'id_res',
  39. 'login',
  40. 'logout',
  41. 'test'),
  42. 'session_types' => array('',
  43. 'DH-SHA1'),
  44. 'bigmath_types' => array('DH-SHA1'),
  45. );
  46. /**
  47. * Defined by OpenID spec
  48. * @name $g
  49. * @global integer $GLOBALS['g']
  50. */
  51. $GLOBALS['g'] = 2;
  52. /**
  53. * Defined by OpenID spec
  54. * @name $p
  55. * @global integer $GLOBALS['p']
  56. */
  57. $GLOBALS['p'] = '155172898181473697471232257763715539915724801966915404479707' .
  58. '7953140576293785419175806512274236981889937278161526466314385615958256881888' .
  59. '8995127215884267541995034125870655654980358010487053768147672651325574704076' .
  60. '5857479291291572334510643245094715007229621094194349783925984760375594985848' .
  61. '253359305585439638443';
  62. // Runmode functions
  63. /**
  64. * Allow the user to accept trust on a URL
  65. * @global array $profile
  66. */
  67. function accept_mode () {
  68. global $profile;
  69. // this is a user session
  70. user_session();
  71. // the user needs refresh urls in their session to access this mode
  72. if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_url']))
  73. error_500('You may not access this mode directly.');
  74. // has the user accepted the trust_root?
  75. $accepted = @strlen($_REQUEST['accepted'])
  76. ? $_REQUEST['accepted']
  77. : null;
  78. // if so, refresh back to post_accept_url
  79. if ($accepted === 'yes') {
  80. $_SESSION['accepted_url'] = $_SESSION['unaccepted_url'];
  81. wrap_redirect($_SESSION['post_accept_url']);
  82. // if they rejected it, return to the client
  83. } elseif ($accepted === 'no') {
  84. wrap_redirect($_SESSION['cancel_accept_url']);
  85. }
  86. // if neither, offer the trust request
  87. $q = strpos($profile['req_url'], '?') ? '&' : '?';
  88. $yes = $profile['req_url'] . $q . 'accepted=yes';
  89. $no = $profile['req_url'] . $q . 'accepted=no';
  90. wrap_html('The client site you are attempting to log into has requested that you trust the following URL:<br/><b>' . $_SESSION['unaccepted_url'] . '</b><br/><br/>Do you wish to continue?<br/><a href="' . $yes . '">Yes</a> | <a href="' . $no . '">No</a>');
  91. }
  92. /** * Perform an association with a consumer
  93. * @global array $known
  94. * @global array $profile
  95. * @global integer $g
  96. * @global integer $p
  97. */
  98. function associate_mode () {
  99. global $g, $known, $p, $profile;
  100. // Validate the request
  101. if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'associate')
  102. error_400();
  103. // Get the request options, using defaults as necessary
  104. $assoc_type = (@strlen($_REQUEST['openid_assoc_type'])
  105. && in_array($_REQUEST['openid_assoc_type'], $known['assoc_types']))
  106. ? $_REQUEST['openid_assoc_type']
  107. : 'HMAC-SHA1';
  108. $session_type = (@strlen($_REQUEST['openid_session_type'])
  109. && in_array($_REQUEST['openid_session_type'], $known['session_types']))
  110. ? $_REQUEST['openid_session_type']
  111. : '';
  112. $dh_modulus = (@strlen($_REQUEST['openid_dh_modulus']))
  113. ? long(base64_decode($_REQUEST['openid_dh_modulus']))
  114. : ($session_type == 'DH-SHA1'
  115. ? $p
  116. : null);
  117. $dh_gen = (@strlen($_REQUEST['openid_dh_gen']))
  118. ? long(base64_decode($_REQUEST['openid_dh_gen']))
  119. : ($session_type == 'DH-SHA1'
  120. ? $g
  121. : null);
  122. $dh_consumer_public = (@strlen($_REQUEST['openid_dh_consumer_public']))
  123. ? $_REQUEST['openid_dh_consumer_public']
  124. : ($session_type == 'DH-SHA1'
  125. ? error_post('dh_consumer_public was not specified')
  126. : null);
  127. $lifetime = time() + $profile['lifetime'];
  128. // Create standard keys
  129. $keys = array(
  130. 'assoc_type' => $assoc_type,
  131. 'expires_in' => $profile['lifetime']
  132. );
  133. // If I can't handle bigmath, default to plaintext sessions
  134. if (in_array($session_type, $known['bigmath_types']) && $profile['use_bigmath'] === false)
  135. $session_type = null;
  136. // Add response keys based on the session type
  137. switch ($session_type) {
  138. case 'DH-SHA1':
  139. // Create the associate id and shared secret now
  140. list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
  141. // Compute the Diffie-Hellman stuff
  142. $private_key = random($dh_modulus);
  143. $public_key = bmpowmod($dh_gen, $private_key, $dh_modulus);
  144. $remote_key = long(base64_decode($dh_consumer_public));
  145. $ss = bmpowmod($remote_key, $private_key, $dh_modulus);
  146. $keys['assoc_handle'] = $assoc_handle;
  147. $keys['session_type'] = $session_type;
  148. $keys['dh_server_public'] = base64_encode(bin($public_key));
  149. $keys['enc_mac_key'] = base64_encode(x_or(sha1_20(bin($ss)), $shared_secret));
  150. break;
  151. default:
  152. // Create the associate id and shared secret now
  153. list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
  154. $keys['assoc_handle'] = $assoc_handle;
  155. $keys['mac_key'] = base64_encode($shared_secret);
  156. }
  157. // Return the keys
  158. wrap_kv($keys);
  159. }
  160. /**
  161. * Perform a user authorization
  162. * @global array $profile
  163. */
  164. function authorize_mode () {
  165. global $profile, $baseurl, $lxa_logged_in;
  166. // this is a user session
  167. user_session();
  168. if ( isset($_GET["board"]) && $_GET["board"] == "redirect" ) {
  169. /*smf_sessionSetup();
  170. $sid = $_SESSION["oid_session"];
  171. session_write_close();
  172. session_id($sid);
  173. session_start();
  174. session_write_close();
  175. session_start();*/
  176. }
  177. // the user needs refresh urls in their session to access this mode
  178. if (! isset($_SESSION['post_auth_url']) || ! isset($_SESSION['cancel_auth_url']))
  179. error_500('You may not access this mode directly.');
  180. $stale = false;
  181. // is the user trying to log in?
  182. if ( $lxa_logged_in && $profile['authorized'] === false) {
  183. debug('Authentication successful for SMF user: ' . $lxa_logged_in);
  184. debug('User session is: ' . session_id());
  185. $_SESSION['auth_username'] = $lxa_logged_in;
  186. $_SESSION['auth_url'] = $profile['idp_url'];
  187. $profile['authorized'] = true;
  188. // return to the refresh url if they get in
  189. wrap_redirect($_SESSION['post_auth_url']);
  190. exit;
  191. /*
  192. // does this make too many failures?
  193. if (strcmp(hexdec($hdr['nc']), 4) > 0 || $_SESSION['failures'] > 4) {
  194. debug('Too many password failures');
  195. error_get($_SESSION['cancel_auth_url'], 'Too many password failures. Double check your authorization realm. You must restart your browser to try again.');
  196. }*/
  197. }
  198. $sid = session_id();
  199. session_regenerate_id(); // just for smf_sessionSetup
  200. $_SESSION = array();
  201. smf_sessionSetup();
  202. $_SESSION['old_url'] = "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] . "&board=redirect";
  203. $_SESSION['login_url'] = $_SESSION['old_url'];
  204. $_SESSION['oid_session'] = $sid;// $oid_session;
  205. smf_sessionWrite($_COOKIE['PHPSESSID'], session_encode());
  206. header('Location: http://www.openlierox.net/forum/index.php?action=login2&sa=salt');
  207. }
  208. /**
  209. * Handle a consumer's request for cancellation.
  210. */
  211. function cancel_mode () {
  212. wrap_html('Request cancelled.');
  213. }
  214. /**
  215. * Handle a consumer's request to see if the user is authenticated
  216. */
  217. function check_authentication_mode () {
  218. // Validate the request
  219. if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'check_authentication')
  220. error_400();
  221. $assoc_handle = @strlen($_REQUEST['openid_assoc_handle'])
  222. ? $_REQUEST['openid_assoc_handle']
  223. : error_post('Missing assoc_handle');
  224. $sig = @strlen($_REQUEST['openid_sig'])
  225. ? $_REQUEST['openid_sig']
  226. : error_post('Missing sig');
  227. $signed = @strlen($_REQUEST['openid_signed'])
  228. ? $_REQUEST['openid_signed']
  229. : error_post('Missing signed');
  230. // Prepare the return keys
  231. $keys = array(
  232. 'openid.mode' => 'id_res'
  233. );
  234. // Invalidate the assoc handle if we need to
  235. if (@strlen($_REQUEST['openid_invalidate_handle'])) {
  236. destroy_assoc_handle($_REQUEST['openid_invalidate_handle']);
  237. $keys['invalidate_handle'] = $_REQUEST['openid_invalidate_handle'];
  238. }
  239. // Validate the sig by recreating the kv pair and signing
  240. $_REQUEST['openid_mode'] = 'id_res';
  241. $tokens = '';
  242. foreach (explode(',', $signed) as $param) {
  243. $post = preg_replace('/\./', '_', $param);
  244. $tokens .= sprintf("%s:%s\n", $param, $_REQUEST['openid_' . $post]);
  245. }
  246. // Add the sreg stuff, if we've got it
  247. if (isset($sreg_required)) {
  248. foreach (explode(',', $sreg_required) as $key) {
  249. if (! isset($sreg[$key]))
  250. continue;
  251. $skey = 'sreg.' . $key;
  252. $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]);
  253. $keys[$skey] = $sreg[$key];
  254. $fields[] = $skey;
  255. }
  256. }
  257. // Look up the consumer's shared_secret and timeout
  258. list ($shared_secret, $expires) = secret($assoc_handle);
  259. // if I can't verify the assoc_handle, or if it's expired
  260. if ($shared_secret == false || (is_numeric($expires) && $expires < time())) {
  261. $keys['is_valid'] = 'false';
  262. } else {
  263. $ok = base64_encode(hmac($shared_secret, $tokens));
  264. $keys['is_valid'] = ($sig == $ok) ? 'true' : 'false';
  265. }
  266. // Return the keys
  267. wrap_kv($keys);
  268. }
  269. /**
  270. * Handle a consumer's request to see if the end user is logged in
  271. * @global array $known
  272. * @global array $profile
  273. * @global array $sreg
  274. */
  275. function checkid ( $wait ) {
  276. debug("checkid: wait? $wait");
  277. global $known, $profile, $sreg;
  278. // This is a user session
  279. user_session();
  280. // Get the options, use defaults as necessary
  281. $return_to = @strlen($_REQUEST['openid_return_to'])
  282. ? $_REQUEST['openid_return_to']
  283. : error_400('Missing return_to');
  284. $identity = @strlen($_REQUEST['openid_identity'])
  285. ? $_REQUEST['openid_identity']
  286. : error_get($return_to, 'Missing identity');
  287. $assoc_handle = @strlen($_REQUEST['openid_assoc_handle'])
  288. ? $_REQUEST['openid_assoc_handle']
  289. : null;
  290. $trust_root = @strlen($_REQUEST['openid_trust_root'])
  291. ? $_REQUEST['openid_trust_root']
  292. : $return_to;
  293. $sreg_required = @strlen($_REQUEST['openid_sreg_required'])
  294. ? $_REQUEST['openid_sreg_required']
  295. : '';
  296. $sreg_optional = @strlen($_REQUEST['openid_sreg_optional'])
  297. ? $_REQUEST['openid_sreg_optional']
  298. : '';
  299. // determine the cancel url
  300. $q = strpos($return_to, '?') ? '&' : '?';
  301. $cancel_url = $return_to . $q . 'openid.mode=cancel';
  302. // required and optional make no difference to us
  303. $sreg_required .= ',' . $sreg_optional;
  304. // do the trust_root analysis
  305. if ($trust_root != $return_to) {
  306. // the urls are not the same, be sure return decends from trust
  307. if (! url_descends($return_to, $trust_root))
  308. error_500('Invalid trust_root: "' . $trust_root . '"');
  309. }
  310. // transfer the user to the url accept mode if they're paranoid
  311. if ($wait == 1 && isset($profile['paranoid']) && $profile['paranoid'] === true && (! session_is_registered('accepted_url') || $_SESSION['accepted_url'] != $trust_root)) {
  312. $_SESSION['cancel_accept_url'] = $cancel_url;
  313. $_SESSION['post_accept_url'] = $profile['req_url'];
  314. $_SESSION['unaccepted_url'] = $trust_root;
  315. debug('Transferring to acceptance mode.');
  316. debug('Cancel URL: ' . $_SESSION['cancel_accept_url']);
  317. debug('Post URL: ' . $_SESSION['post_accept_url']);
  318. $q = strpos($profile['idp_url'], '?') ? '&' : '?';
  319. wrap_redirect($profile['idp_url'] . $q . 'openid.mode=accept');
  320. }
  321. /*
  322. TODO: this fails in the beginning where we don't know the identity yet.
  323. so what is the meaning of this code?
  324. // make sure i am this identifier
  325. if ($identity != $profile['idp_url']) {
  326. debug("Invalid identity: $identity");
  327. debug("IdP URL: " . $profile['idp_url']);
  328. error_get($return_to, "Invalid identity: '$identity'");
  329. }*/
  330. // begin setting up return keys
  331. $keys = array(
  332. 'mode' => 'id_res'
  333. );
  334. // if the user is not logged in, transfer to the authorization mode
  335. if ($profile['authorized'] === false /* || $identity != $_SESSION['auth_url'] */) {
  336. // users can only be logged in to one url at a time
  337. $_SESSION['auth_username'] = null;
  338. $_SESSION['auth_url'] = null;
  339. if ($wait) {
  340. $_SESSION['cancel_auth_url'] = $cancel_url;
  341. $_SESSION['post_auth_url'] = $profile['req_url'];
  342. debug('Transferring to authorization mode.');
  343. debug('Cancel URL: ' . $_SESSION['cancel_auth_url']);
  344. debug('Post URL: ' . $_SESSION['post_auth_url']);
  345. $q = strpos($profile['idp_url'], '?') ? '&' : '?';
  346. wrap_redirect($profile['idp_url'] . $q . 'openid.mode=authorize');
  347. } else {
  348. $keys['user_setup_url'] = $profile['idp_url'];
  349. }
  350. // the user is logged in
  351. } else {
  352. // remove the refresh URLs if set
  353. unset($_SESSION['cancel_auth_url']);
  354. unset($_SESSION['post_auth_url']);
  355. // check the assoc handle
  356. list($shared_secret, $expires) = secret($assoc_handle);
  357. // if I can't verify the assoc_handle, or if it's expired
  358. if ($shared_secret == false || (is_numeric($expires) && $expires < time())) {
  359. debug("Session expired or missing key: $expires < " . time());
  360. if ($assoc_handle != null) {
  361. $keys['invalidate_handle'] = $assoc_handle;
  362. destroy_assoc_handle($assoc_handle);
  363. }
  364. $lifetime = time() + $profile['lifetime'];
  365. list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
  366. }
  367. $keys['identity'] = $profile['idp_url'];
  368. $keys['assoc_handle'] = $assoc_handle;
  369. $keys['return_to'] = $return_to;
  370. $fields = array_keys($keys);
  371. $tokens = '';
  372. foreach ($fields as $key)
  373. $tokens .= sprintf("%s:%s\n", $key, $keys[$key]);
  374. // add sreg keys
  375. foreach (explode(',', $sreg_required) as $key) {
  376. if (! isset($sreg[$key]))
  377. continue;
  378. $skey = 'sreg.' . $key;
  379. $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]);
  380. $keys[$skey] = $sreg[$key];
  381. $fields[] = $skey;
  382. }
  383. $keys['signed'] = implode(',', $fields);
  384. $keys['sig'] = base64_encode(hmac($shared_secret, $tokens));
  385. }
  386. wrap_keyed_redirect($return_to, $keys);
  387. }
  388. /**
  389. * Handle a consumer's request to see if the user is already logged in
  390. */
  391. function checkid_immediate_mode () {
  392. if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_immediate')
  393. error_500();
  394. checkid(false);
  395. }
  396. /**
  397. * Handle a consumer's request to see if the user is logged in, but be willing
  398. * to wait for them to perform a login if they're not
  399. */
  400. function checkid_setup_mode () {
  401. if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_setup')
  402. error_500();
  403. checkid(true);
  404. }
  405. /**
  406. * Handle errors
  407. */
  408. function error_mode () {
  409. isset($_REQUEST['openid_error'])
  410. ? wrap_html($_REQUEST['openid_error'])
  411. : error_500();
  412. }
  413. /**
  414. * Show a user if they are logged in or not
  415. * @global array $profile
  416. */
  417. function id_res_mode () {
  418. global $profile;
  419. user_session();
  420. if ($profile['authorized'])
  421. wrap_html('You are logged in as ' . $_SESSION['auth_username']);
  422. wrap_html('You are not logged in');
  423. }
  424. /**
  425. * Allow a user to perform a static login
  426. * @global array $profile
  427. */
  428. function login_mode () {
  429. global $profile;
  430. user_session();
  431. if ($profile['authorized'])
  432. id_res_mode();
  433. $keys = array(
  434. 'mode' => 'checkid_setup',
  435. 'identity' => $profile['idp_url'],
  436. 'return_to' => $profile['idp_url']
  437. );
  438. wrap_keyed_redirect($profile['idp_url'], $keys);
  439. }
  440. /**
  441. * Allow a user to perform a static logout
  442. * @global array $profile
  443. */
  444. function logout_mode () {
  445. global $profile;
  446. user_session();
  447. if (! $profile['authorized'])
  448. wrap_html('You were not logged in');
  449. $_SESSION = array();
  450. session_destroy();
  451. debug('User session destroyed.');
  452. header('HTTP/1.0 401 Unauthorized');
  453. wrap_redirect($profile['idp_url']);
  454. }
  455. /**
  456. * The default information screen
  457. * @global array $profile
  458. */
  459. function no_mode () {
  460. global $profile;
  461. wrap_html('This is an OpenID server endpoint. For more information, see http://openid.net/<br/>Server: <b>' . $profile['idp_url'] . '</b><br/>Realm: <b>' . $profile['php_realm'] . '</b><br/><a href="' . $profile['idp_url'] . '?openid.mode=login">Login</a>' . ($profile['allow_test'] === true ? ' | <a href="' . $profile['idp_url'] . '?openid.mode=test">Test</a>' : null));
  462. }
  463. /**
  464. * Testing for setup
  465. * @global array $profile
  466. */
  467. function test_mode () {
  468. global $profile, $p, $g;
  469. if ($profile['allow_test'] != true)
  470. error_403();
  471. @ini_set('max_execution_time', 180);
  472. $test_expire = time() + 120;
  473. $test_ss_enc = 'W7hvmld2yEYdDb0fHfSkKhQX+PM=';
  474. $test_ss = base64_decode($test_ss_enc);
  475. $test_token = "alpha:bravo\ncharlie:delta\necho:foxtrot";
  476. $test_server_private = '11263846781670293092494395517924811173145217135753406847875706165886322533899689335716152496005807017390233667003995430954419468996805220211293016296351031812246187748601293733816011832462964410766956326501185504714561648498549481477143603650090931135412673422192550825523386522507656442905243832471167330268';
  477. $test_client_public = base64_decode('AL63zqI5a5p8HdXZF5hFu8p+P9GOb816HcHuvNOhqrgkKdA3fO4XEzmldlb37nv3+xqMBgWj6gxT7vfuFerEZLBvuWyVvR7IOGZmx0BAByoq3fxYd3Fpe2Coxngs015vK37otmH8e83YyyGo5Qua/NAf13yz1PVuJ5Ctk7E+YdVc');
  478. $res = array();
  479. // bcmath
  480. $res['bcmath'] = extension_loaded('bcmath')
  481. ? 'pass' : 'warn - not loaded';
  482. // gmp
  483. if ($profile['allow_gmp']) {
  484. $res['gmp'] = extension_loaded('gmp')
  485. ? 'pass' : 'warn - not loaded';
  486. } else {
  487. $res['gmp'] = 'pass - n/a';
  488. }
  489. // sys_get_temp_dir
  490. $res['logfile'] = is_writable($profile['logfile'])
  491. ? 'pass' : "warn - log is not writable";
  492. // session & new_assoc
  493. user_session();
  494. list($test_assoc, $test_new_ss) = new_assoc($test_expire);
  495. $res['session'] = ($test_assoc != session_id())
  496. ? 'pass' : 'fail';
  497. // secret
  498. @session_unregister('shared_secret');
  499. list($check, $check2) = secret($test_assoc);
  500. $res['secret'] = ($check == $test_new_ss)
  501. ? 'pass' : 'fail';
  502. // expire
  503. $res['expire'] = ($check2 <= $test_expire)
  504. ? 'pass' : 'fail';
  505. // base64
  506. $res['base64'] = (base64_encode($test_ss) == $test_ss_enc)
  507. ? 'pass' : 'fail';
  508. // hmac
  509. $test_sig = base64_decode('/VXgHvZAOdoz/OTa5+XJXzSGhjs=');
  510. $check = hmac($test_ss, $test_token);
  511. $res['hmac'] = ($check == $test_sig)
  512. ? 'pass' : sprintf("fail - '%s'", base64_encode($check));
  513. if ($profile['use_bigmath']) {
  514. // bigmath powmod
  515. $test_server_public = '102773334773637418574009974502372885384288396853657336911033649141556441102566075470916498748591002884433213640712303846640842555822818660704173387461364443541327856226098159843042567251113889701110175072389560896826887426539315893475252988846151505416694218615764823146765717947374855806613410142231092856731';
  516. $check = bmpowmod($g, $test_server_private, $p);
  517. $res['bmpowmod-1'] = ($check == $test_server_public)
  518. ? 'pass' : sprintf("fail - '%s'", $check);
  519. // long
  520. $test_client_long = '133926731803116519408547886573524294471756220428015419404483437186057383311250738749035616354107518232016420809434801736658109316293127101479053449990587221774635063166689561125137927607200322073086097478667514042144489248048756916881344442393090205172004842481037581607299263456852036730858519133859409417564';
  521. $res['long'] = (long($test_client_public) == $test_client_long)
  522. ? 'pass' : 'fail';
  523. // bigmath powmod 2
  524. $test_client_share = '19333275433742428703546496981182797556056709274486796259858099992516081822015362253491867310832140733686713353304595602619444380387600756677924791671971324290032515367930532292542300647858206600215875069588627551090223949962823532134061941805446571307168890255137575975911397744471376862555181588554632928402';
  525. $check = bmpowmod($test_client_long, $test_server_private, $p);
  526. $res['bmpowmod-2'] = ($check == $test_client_share)
  527. ? 'pass' : sprintf("fail - '%s'", $check);
  528. // bin
  529. $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI=');
  530. $check = bin($test_client_share);
  531. $res['bin'] = ($check == $test_client_mac_s1)
  532. ? 'pass' : sprintf("fail - '%s'", base64_encode($check));
  533. } else {
  534. $res['bigmath'] = 'fail - big math functions are not available.';
  535. }
  536. // sha1_20
  537. $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI=');
  538. $test_client_mac_s2 = base64_decode('0Mb2t9d/HvAZyuhbARJPYdx3+v4=');
  539. $check = sha1_20($test_client_mac_s1);
  540. $res['sha1_20'] = ($check == $test_client_mac_s2)
  541. ? 'pass' : sprintf("fail - '%s'", base64_encode($check));
  542. // x_or
  543. $test_client_mac_s3 = base64_decode('i36ZLYAJ1rYEx1VEHObrS8hgAg0=');
  544. $check = x_or($test_client_mac_s2, $test_ss);
  545. $res['x_or'] = ($check == $test_client_mac_s3)
  546. ? 'pass' : sprintf("fail - '%s'", base64_encode($check));
  547. $out = "<table border=1 cellpadding=4>\n";
  548. foreach ($res as $test => $stat) {
  549. $code = substr($stat, 0, 4);
  550. $color = ($code == 'pass') ? '#9f9'
  551. : (($code == 'warn') ? '#ff9' : '#f99');
  552. $out .= sprintf("<tr><th>%s</th><td style='background:%s'>%s</td></tr>\n", $test, $color, $stat);
  553. }
  554. $out .= "</table>";
  555. wrap_html( $out );
  556. }
  557. // Support functions
  558. /**
  559. * Prefix the keys of an array with 'openid.'
  560. * @param array $array
  561. * @return array
  562. */
  563. function append_openid ($array) {
  564. $keys = array_keys($array);
  565. $vals = array_values($array);
  566. $r = array();
  567. for ($i=0; $i<sizeof($keys); $i++)
  568. $r['openid.' . $keys[$i]] = $vals[$i];
  569. return $r;
  570. }
  571. /**
  572. * Create a big math addition function
  573. * @param string $l
  574. * @param string $r
  575. * @return string
  576. * @url http://www.icosaedro.it/bigint Inspired by
  577. */
  578. function bmadd($l, $r) {
  579. if (function_exists('bcadd'))
  580. return bcadd($l, $r);
  581. global $profile;
  582. if ($profile['use_gmp'])
  583. return gmp_strval(gmp_add($l, $r));
  584. $l = strval($l); $r = strval($r);
  585. $ll = strlen($l); $rl = strlen($r);
  586. if ($ll < $rl) {
  587. $l = str_repeat("0", $rl-$ll) . $l;
  588. $o = $rl;
  589. } elseif ( $ll > $rl ) {
  590. $r = str_repeat("0", $ll-$rl) . $r;
  591. $o = $ll;
  592. } else {
  593. $o = $ll;
  594. }
  595. $v = '';
  596. $carry = 0;
  597. for ($i = $o-1; $i >= 0; $i--) {
  598. $d = (int)$l[$i] + (int)$r[$i] + $carry;
  599. if ($d <= 9) {
  600. $carry = 0;
  601. } else {
  602. $carry = 1;
  603. $d -= 10;
  604. }
  605. $v = (string) $d . $v;
  606. }
  607. if ($carry > 0)
  608. $v = "1" . $v;
  609. return $v;
  610. }
  611. /**
  612. * Create a big math comparison function
  613. * @param string $l
  614. * @param string $r
  615. * @return string
  616. */
  617. function bmcomp($l, $r) {
  618. if (function_exists('bccomp'))
  619. return bccomp($l, $r);
  620. global $profile;
  621. if ($profile['use_gmp'])
  622. return gmp_strval(gmp_cmp($l, $r));
  623. $l = strval($l); $r = strval($r);
  624. $ll = strlen($l); $lr = strlen($r);
  625. if ($ll != $lr)
  626. return ($ll > $lr) ? 1 : -1;
  627. return strcmp($l, $r);
  628. }
  629. /**
  630. * Create a big math division function
  631. * @param string $l
  632. * @param string $r
  633. * @param int $z
  634. * @return string
  635. * @url http://www.icosaedro.it/bigint Inspired by
  636. */
  637. function bmdiv($l, $r, $z = 0) {
  638. if (function_exists('bcdiv'))
  639. return ($z == 0) ? bcdiv($l, $r) : bcmod($l, $r);
  640. global $profile;
  641. if ($profile['use_gmp'])
  642. return gmp_strval(($z == 0) ? gmp_div_q($l, $r) : gmp_mod($l, $r));
  643. $l = strval($l); $r = strval($r);
  644. $v = '0';
  645. while (true) {
  646. if( bmcomp($l, $r) < 0 )
  647. break;
  648. $delta = strlen($l) - strlen($r);
  649. if ($delta >= 1) {
  650. $zeroes = str_repeat("0", $delta);
  651. $r2 = $r . $zeroes;
  652. if (strcmp($l, $r2) >= 0) {
  653. $v = bmadd($v, "1" . $zeroes);
  654. $l = bmsub($l, $r2);
  655. } else {
  656. $zeroes = str_repeat("0", $delta - 1);
  657. $v = bmadd($v, "1" . $zeroes);
  658. $l = bmsub($l, $r . $zeroes);
  659. }
  660. } else {
  661. $l = bmsub($l, $r);
  662. $v = bmadd($v, "1");
  663. }
  664. }
  665. return ($z == 0) ? $v : $l;
  666. }
  667. /**
  668. * Create a big math multiplication function
  669. * @param string $l
  670. * @param string $r
  671. * @return string
  672. * @url http://www.icosaedro.it/bigint Inspired by
  673. */
  674. function bmmul($l, $r) {
  675. if (function_exists('bcmul'))
  676. return bcmul($l, $r);
  677. global $profile;
  678. if ($profile['use_gmp'])
  679. return gmp_strval(gmp_mul($l, $r));
  680. $l = strval($l); $r = strval($r);
  681. $v = '0';
  682. $z = '';
  683. for( $i = strlen($r)-1; $i >= 0; $i-- ){
  684. $bd = (int) $r[$i];
  685. $carry = 0;
  686. $p = "";
  687. for( $j = strlen($l)-1; $j >= 0; $j-- ){
  688. $ad = (int) $l[$j];
  689. $pd = $ad * $bd + $carry;
  690. if( $pd <= 9 ){
  691. $carry = 0;
  692. } else {
  693. $carry = (int) ($pd / 10);
  694. $pd = $pd % 10;
  695. }
  696. $p = (string) $pd . $p;
  697. }
  698. if( $carry > 0 )
  699. $p = (string) $carry . $p;
  700. $p = $p . $z;
  701. $z .= "0";
  702. $v = bmadd($v, $p);
  703. }
  704. return $v;
  705. }
  706. /**
  707. * Create a big math modulus function
  708. * @param string $value
  709. * @param string $mod
  710. * @return string
  711. */
  712. function bmmod( $value, $mod ) {
  713. if (function_exists('bcmod'))
  714. return bcmod($value, $mod);
  715. global $profile;
  716. if ($profile['use_gmp'])
  717. return gmp_strval(gmp_mod($value, $mod));
  718. $r = bmdiv($value, $mod, 1);
  719. return $r;
  720. }
  721. /**
  722. * Create a big math power function
  723. * @param string $value
  724. * @param string $exponent
  725. * @return string
  726. */
  727. function bmpow ($value, $exponent) {
  728. if (function_exists('bcpow'))
  729. return bcpow($value, $exponent);
  730. global $profile;
  731. if ($profile['use_gmp'])
  732. return gmp_strval(gmp_pow($value, $exponent));
  733. $r = '1';
  734. while ($exponent) {
  735. $r = bmmul($r, $value, 100);
  736. $exponent--;
  737. }
  738. return (string)rtrim($r, '0.');
  739. }
  740. /**
  741. * Create a big math 'powmod' function
  742. * @param string $value
  743. * @param string $exponent
  744. * @param string $mod
  745. * @return string
  746. * @url http://php.net/manual/en/function.bcpowmod.php#72704 Borrowed from
  747. */
  748. function bmpowmod ($value, $exponent, $mod) {
  749. if (function_exists('bcpowmod'))
  750. return bcpowmod($value, $exponent, $mod);
  751. global $profile;
  752. if ($profile['use_gmp'])
  753. return gmp_strval(gmp_powm($value, $exponent, $mod));
  754. $r = '';
  755. while ($exponent != '0') {
  756. $t = bmmod($exponent, '4096');
  757. $r = substr("000000000000" . decbin(intval($t)), -12) . $r;
  758. $exponent = bmdiv($exponent, '4096');
  759. }
  760. $r = preg_replace("!^0+!","",$r);
  761. if ($r == '')
  762. $r = '0';
  763. $value = bmmod($value, $mod);
  764. $erb = strrev($r);
  765. $q = '1';
  766. $a[0] = $value;
  767. for ($i = 1; $i < strlen($erb); $i++) {
  768. $a[$i] = bmmod( bmmul($a[$i-1], $a[$i-1]), $mod );
  769. }
  770. for ($i = 0; $i < strlen($erb); $i++) {
  771. if ($erb[$i] == "1") {
  772. $q = bmmod( bmmul($q, $a[$i]), $mod );
  773. }
  774. }
  775. return($q);
  776. }
  777. /**
  778. * Create a big math subtraction function
  779. * @param string $l
  780. * @param string $r
  781. * @return string
  782. * @url http://www.icosaedro.it/bigint Inspired by
  783. */
  784. function bmsub($l, $r) {
  785. if (function_exists('bcsub'))
  786. return bcsub($l, $r);
  787. global $profile;
  788. if ($profile['use_gmp'])
  789. return gmp_strval(gmp_sub($l, $r));
  790. $l = strval($l); $r = strval($r);
  791. $ll = strlen($l); $rl = strlen($r);
  792. if ($ll < $rl) {
  793. $l = str_repeat("0", $rl-$ll) . $l;
  794. $o = $rl;
  795. } elseif ( $ll > $rl ) {
  796. $r = str_repeat("0", $ll-$rl) . (string)$r;
  797. $o = $ll;
  798. } else {
  799. $o = $ll;
  800. }
  801. if (strcmp($l, $r) >= 0) {
  802. $sign = '';
  803. } else {
  804. $x = $l; $l = $r; $r = $x;
  805. $sign = '-';
  806. }
  807. $v = '';
  808. $carry = 0;
  809. for ($i = $o-1; $i >= 0; $i--) {
  810. $d = ($l[$i] - $r[$i]) - $carry;
  811. if ($d < 0) {
  812. $carry = 1;
  813. $d += 10;
  814. } else {
  815. $carry = 0;
  816. }
  817. $v = (string) $d . $v;
  818. }
  819. return $sign . ltrim($v, '0');
  820. }
  821. /**
  822. * Get a binary value
  823. * @param integer $n
  824. * @return string
  825. * @url http://openidenabled.com Borrowed from PHP-OpenID
  826. */
  827. function bin ($n) {
  828. $bytes = array();
  829. while (bmcomp($n, 0) > 0) {
  830. array_unshift($bytes, bmmod($n, 256));
  831. $n = bmdiv($n, bmpow(2,8));
  832. }
  833. if ($bytes && ($bytes[0] > 127))
  834. array_unshift($bytes, 0);
  835. $b = '';
  836. foreach ($bytes as $byte)
  837. $b .= pack('C', $byte);
  838. return $b;
  839. }
  840. /**
  841. * Debug logging
  842. * @param mixed $x
  843. * @param string $m
  844. */
  845. function debug ($x, $m = null) {
  846. global $profile;
  847. if (! isset($profile['debug']) || $profile['debug'] === false)
  848. return true;
  849. if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile']))
  850. error_500('Cannot write to debug log: ' . $profile['logfile']);
  851. if (is_array($x)) {
  852. ob_start();
  853. print_r($x);
  854. $x = $m . ($m != null ? "\n" : '') . ob_get_clean();
  855. } else {
  856. $x .= "\n";
  857. }
  858. error_log($x . "\n", 3, $profile['logfile']);
  859. }
  860. /**
  861. * Destroy a consumer's assoc handle
  862. * @param string $id
  863. */
  864. function destroy_assoc_handle ( $id ) {
  865. debug("Destroying session: $id");
  866. $sid = session_id();
  867. session_write_close();
  868. session_id($id);
  869. session_start();
  870. session_destroy();
  871. session_id($sid);
  872. session_start();
  873. }
  874. /**
  875. * Return an error message to the user
  876. * @param string $message
  877. */
  878. function error_400 ( $message = 'Bad Request' ) {
  879. header("HTTP/1.1 400 Bad Request");
  880. wrap_html($message);
  881. }
  882. /**
  883. * Return an error message to the user
  884. * @param string $message
  885. */
  886. function error_403 ( $message = 'Forbidden' ) {
  887. header("HTTP/1.1 403 Forbidden");
  888. wrap_html($message);
  889. }
  890. /**
  891. * Return an error message to the user
  892. * @param string $message
  893. */
  894. function error_500 ( $message = 'Internal Server Error' ) {
  895. header("HTTP/1.1 500 Internal Server Error");
  896. wrap_html($message);
  897. }
  898. /**
  899. * Return an error message to the consumer
  900. * @param string $message
  901. */
  902. function error_get ( $url, $message = 'Bad Request') {
  903. wrap_keyed_redirect($url, array('mode' => 'error', 'error' => $message));
  904. }
  905. /**
  906. * Return an error message to the consumer
  907. * @param string $message
  908. */
  909. function error_post ( $message = 'Bad Request' ) {
  910. header("HTTP/1.1 400 Bad Request");
  911. echo ('error:' . $message);
  912. exit(0);
  913. }
  914. /**
  915. * Do an HMAC
  916. * @param string $key
  917. * @param string $data
  918. * @param string $hash
  919. * @return string
  920. * @url http://php.net/manual/en/function.sha1.php#39492 Borrowed from
  921. */
  922. function hmac($key, $data, $hash = 'sha1_20') {
  923. $blocksize=64;
  924. if (strlen($key) > $blocksize)
  925. $key = $hash($key);
  926. $key = str_pad($key, $blocksize,chr(0x00));
  927. $ipad = str_repeat(chr(0x36),$blocksize);
  928. $opad = str_repeat(chr(0x5c),$blocksize);
  929. $h1 = $hash(($key ^ $ipad) . $data);
  930. $hmac = $hash(($key ^ $opad) . $h1);
  931. return $hmac;
  932. }
  933. if (! function_exists('http_build_query')) {
  934. /**
  935. * Create function if missing
  936. * @param array $array
  937. * @return string
  938. */
  939. function http_build_query ($array) {
  940. $r = array();
  941. foreach ($array as $key => $val)
  942. $r[] = sprintf('%s=%s', urlencode($key), urlencode($val));
  943. return implode('&', $r);
  944. }}
  945. /**
  946. * Turn a binary back into a long
  947. * @param string $b
  948. * @return integer
  949. * @url http://openidenabled.com Borrowed from PHP-OpenID
  950. */
  951. function long($b) {
  952. $bytes = array_merge(unpack('C*', $b));
  953. $n = 0;
  954. foreach ($bytes as $byte) {
  955. $n = bmmul($n, bmpow(2,8));
  956. $n = bmadd($n, $byte);
  957. }
  958. return $n;
  959. }
  960. /**
  961. * Create a new consumer association
  962. * @param integer $expiration
  963. * @return array
  964. */
  965. function new_assoc ( $expiration ) {
  966. if (isset($_SESSION) && is_array($_SESSION)) {
  967. $sid = session_id();
  968. $dat = session_encode();
  969. session_write_close();
  970. }
  971. session_start();
  972. session_regenerate_id('false');
  973. $id = session_id();
  974. $shared_secret = new_secret();
  975. debug('Started new assoc session: ' . $id);
  976. $_SESSION = array();
  977. $_SESSION['expiration'] = $expiration;
  978. $_SESSION['shared_secret'] = base64_encode($shared_secret);
  979. session_write_close();
  980. if (isset($sid)) {
  981. session_id($sid);
  982. session_start();
  983. $_SESSION = array();
  984. session_decode($dat);
  985. }
  986. return array($id, $shared_secret);
  987. }
  988. /**
  989. * Create a new shared secret
  990. * @return string
  991. */
  992. function new_secret () {
  993. $r = '';
  994. for($i=0; $i<20; $i++)
  995. $r .= chr(mt_rand(0, 255));
  996. debug("Generated new key: hash = '" . md5($r) . "', length = '" . strlen($r) . "'");
  997. return $r;
  998. }
  999. /**
  1000. * Random number generation
  1001. * @param integer max
  1002. * @return integer
  1003. */
  1004. function random ( $max ) {
  1005. if (strlen($max) < 4)
  1006. return mt_rand(1, $max - 1);
  1007. $r = '';
  1008. for($i=1; $i<strlen($max) - 1; $i++)
  1009. $r .= mt_rand(0,9);
  1010. $r .= mt_rand(1,9);
  1011. return $r;
  1012. }
  1013. /**
  1014. * Get the shared secret and expiration time for the specified assoc_handle
  1015. * @param string $handle assoc_handle to look up
  1016. * @return array (shared_secret, expiration_time)
  1017. */
  1018. function secret ( $handle ) {
  1019. if (! preg_match('/^\w+$/', $handle))
  1020. return array(false, 0);
  1021. if (isset($_SESSION) && is_array($_SESSION)) {
  1022. $sid = session_id();
  1023. $dat = session_encode();
  1024. session_write_close();
  1025. }
  1026. session_id($handle);
  1027. session_start();
  1028. debug('Started session to acquire key: ' . session_id());
  1029. $secret = isset($_SESSION['shared_secret'])
  1030. ? base64_decode($_SESSION['shared_secret'])
  1031. : false;
  1032. $expiration = isset($_SESSION['expiration'])
  1033. ? $_SESSION['expiration']
  1034. : null;
  1035. session_write_close();
  1036. if (isset($sid)) {
  1037. session_id($sid);
  1038. session_start();
  1039. $_SESSION = array();
  1040. session_decode($dat);
  1041. }
  1042. debug("Found key: hash = '" . md5($secret) . "', length = '" . strlen($secret) . "', expiration = '$expiration'");
  1043. return array($secret, $expiration);
  1044. }
  1045. /**
  1046. * Do an internal self check
  1047. * @global array $profile
  1048. * @global array $sreg
  1049. */
  1050. function self_check () {
  1051. global $profile, $sreg;
  1052. if (! isset($profile) || ! is_array($profile))
  1053. error_500('No configuration found, you shouldn\'t access this file directly.');
  1054. if (version_compare(phpversion(), '4.2.0', 'lt'))
  1055. error_500('The required minimum version of PHP is 4.2.0, you are running ' . phpversion());
  1056. $extension_r = array('session', 'pcre');
  1057. foreach ($extension_r as $x) {
  1058. if (! extension_loaded($x))
  1059. @dl($x);
  1060. if (! extension_loaded($x))
  1061. error_500("Required extension '$x' is missing.");
  1062. }
  1063. /*$extension_b = array('suhosin');
  1064. foreach ($extension_b as $x) {
  1065. if (extension_loaded($x) &! $profile["allow_$x"])
  1066. error_500("phpMyID is not compatible with '$x'");
  1067. }*/
  1068. $keys = array('auth_username', 'auth_password');
  1069. foreach ($keys as $key) {
  1070. if (! array_key_exists($key, $profile))
  1071. error_500("'$key' is missing from your profile.");
  1072. }
  1073. if (! isset($sreg) || ! is_array($sreg))
  1074. $sreg = array();
  1075. }
  1076. /**
  1077. * Do SHA1 20 byte encryption
  1078. * @param string $v
  1079. * @return string
  1080. * @url http://openidenabled.com Borrowed from PHP-OpenID
  1081. */
  1082. function sha1_20 ($v) {
  1083. if (version_compare(phpversion(), '5.0.0', 'ge'))
  1084. return sha1($v, true);
  1085. $hex = sha1($v);
  1086. $r = '';
  1087. for ($i = 0; $i < 40; $i += 2) {
  1088. $hexcode = substr($hex, $i, 2);
  1089. $charcode = base_convert($hexcode, 16, 10);
  1090. $r .= chr($charcode);
  1091. }
  1092. return $r;
  1093. }
  1094. /**
  1095. * Look for the point of differentiation in two strings
  1096. * @param string $a
  1097. * @param string $b
  1098. * @return int
  1099. */
  1100. function str_diff_at ($a, $b) {
  1101. if ($a == $b)
  1102. return -1;
  1103. $n = min(strlen($a), strlen($b));
  1104. for ($i = 0; $i < $n; $i++)
  1105. if ($a[$i] != $b[$i])
  1106. return $i;
  1107. return $n;
  1108. }
  1109. if (! function_exists('sys_get_temp_dir') && ini_get('open_basedir') == false) {
  1110. /**
  1111. * Create function if missing
  1112. * @return string
  1113. */
  1114. function sys_get_temp_dir () {
  1115. $keys = array('TMP', 'TMPDIR', 'TEMP');
  1116. foreach ($keys as $key) {
  1117. if (isset($_ENV[$key]) && is_dir($_ENV[$key]) && is_writable($_ENV[$key]))
  1118. return realpath($_ENV[$key]);
  1119. }
  1120. $tmp = tempnam(false, null);
  1121. if (file_exists($tmp)) {
  1122. $dir = realpath(dirname($tmp));
  1123. unlink($tmp);
  1124. return realpath($dir);
  1125. }
  1126. return realpath(dirname(__FILE__));
  1127. }} elseif (! function_exists('sys_get_temp_dir')) {
  1128. function sys_get_temp_dir () {
  1129. return realpath(dirname(__FILE__));
  1130. }}
  1131. /**
  1132. * Determine if a child URL actually decends from the parent, and that the
  1133. * parent is a good URL.
  1134. * THIS IS EXPERIMENTAL
  1135. * @param string $parent
  1136. * @param string $child
  1137. * @return bool
  1138. */
  1139. function url_descends ( $child, $parent ) {
  1140. if ($child == $parent)
  1141. return true;
  1142. $keys = array();
  1143. $parts = array();
  1144. $req = array('scheme', 'host');
  1145. $bad = array('fragment', 'pass', 'user');
  1146. foreach (array('parent', 'child') as $name) {
  1147. $parts[$name] = @parse_url($$name);
  1148. if ($parts[$name] === false)
  1149. return false;
  1150. $keys[$name] = array_keys($parts[$name]);
  1151. if (array_intersect($keys[$name], $req) != $req)
  1152. return false;
  1153. if (array_intersect($keys[$name], $bad) != array())
  1154. return false;
  1155. if (! preg_match('/^https?$/i', strtolower($parts[$name]['scheme'])))
  1156. return false;
  1157. if (! array_key_exists('port', $parts[$name]))
  1158. $parts[$name]['port'] = (strtolower($parts[$name]['scheme']) == 'https') ? 443 : 80;
  1159. if (! array_key_exists('path', $parts[$name]))
  1160. $parts[$name]['path'] = '/';
  1161. }
  1162. // port and scheme must match
  1163. if ($parts['parent']['scheme'] != $parts['child']['scheme'] ||
  1164. $parts['parent']['port'] != $parts['child']['port'])
  1165. return false;
  1166. // compare the hosts by reversing the strings
  1167. $cr_host = strtolower(strrev($parts['child']['host']));
  1168. $pr_host = strtolower(strrev($parts['parent']['host']));
  1169. $break = str_diff_at($cr_host, $pr_host);
  1170. if ($break >= 0 && ($pr_host[$break] != '*' || substr_count(substr($pr_host, 0, $break), '.') < 2))
  1171. return false;
  1172. // now compare the paths
  1173. $break = str_diff_at($parts['child']['path'], $parts['parent']['path']);
  1174. if ($break >= 0
  1175. && ($break < strlen($parts['parent']['path']) && $parts['parent']['path'][$break] != '*')
  1176. || ($break > strlen($parts['child']['path'])))
  1177. return false;
  1178. return true;
  1179. }
  1180. /**
  1181. * Create a user session
  1182. * @global array $profile
  1183. * @global array $proto
  1184. */
  1185. function user_session () {
  1186. global $proto, $profile;
  1187. if(isset($_COOKIE['PHPSESSID'])) {
  1188. session_decode(smf_sessionRead($_COOKIE['PHPSESSID']));
  1189. if ( isset($_SESSION["oid_session"]) )
  1190. $sid = $_SESSION["oid_session"];
  1191. session_destroy();
  1192. }
  1193. session_name('phpMyID_Server');
  1194. @session_start();
  1195. if(isset($sid)) {
  1196. session_write_close();
  1197. session_id($sid);
  1198. session_start();
  1199. }
  1200. // Debug note: If we get a redirect loop, it is because we don't see that we have already authorized.
  1201. // Also check existing cookies in your browser, maybe just delete them to be sure.
  1202. $profile['authorized'] = (isset($_SESSION['auth_username']) && lxa_logged_in()) ? true : false;
  1203. debug('Started user session: ' . session_id() . ' Auth? ' . $profile['authorized']);
  1204. }
  1205. /**
  1206. * Return HTML
  1207. * @global string $charset
  1208. * @param string $message
  1209. */
  1210. function wrap_html ( $message ) {
  1211. global $charset, $profile;
  1212. header('Content-Type: text/html; charset=' . $charset);
  1213. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  1214. <html>
  1215. <head>
  1216. <title>phpMyID</title>
  1217. <link rel="openid.server" href="' . $profile['req_url'] . '" />
  1218. <link rel="openid.delegate" href="' . $profile['idp_url'] . '" />
  1219. ' . implode("\n", $profile['opt_headers']) . '
  1220. <meta name="charset" content="' . $charset . '" />
  1221. <meta name="robots" content="noindex,nofollow" />
  1222. </head>
  1223. <body>
  1224. <p>' . $message . '</p>
  1225. </body>
  1226. </html>
  1227. ';
  1228. exit(0);
  1229. }
  1230. /**
  1231. * Return a key-value pair in plain text
  1232. * @global string $charset
  1233. * @param array $keys
  1234. */
  1235. function wrap_kv ( $keys ) {
  1236. global $charset;
  1237. debug($keys, 'Wrapped key/vals');
  1238. header('Content-Type: text/plain; charset=' . $charset);
  1239. foreach ($keys as $key => $value)
  1240. printf("%s:%s\n", $key, $value);
  1241. exit(0);
  1242. }
  1243. /**
  1244. * Redirect, with OpenID keys
  1245. * @param string $url
  1246. * @param array @keys
  1247. */
  1248. function wrap_keyed_redirect ($url, $keys) {
  1249. $keys = append_openid($keys);
  1250. debug($keys, 'Location keys');
  1251. $q = strpos($url, '?') ? '&' : '?';
  1252. wrap_redirect($url . $q . http_build_query($keys));
  1253. }
  1254. /**
  1255. * Redirect the browser
  1256. * @global string $charset
  1257. * @param string $url
  1258. */
  1259. function wrap_redirect ($url) {
  1260. header('HTTP/1.1 302 Found');
  1261. header('Location: ' . $url);
  1262. debug('Location: ' . $url);
  1263. exit(0);
  1264. }
  1265. /**
  1266. * Return an HTML refresh
  1267. * @global string $charset
  1268. * @param string $url
  1269. */
  1270. function wrap_refresh ($url) {
  1271. global $charset;
  1272. header('Content-Type: text/html; charset=' . $charset);
  1273. echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  1274. <html>
  1275. <head>
  1276. <title>phpMyID</title>
  1277. <meta http-equiv="refresh" content="0;url=' . $url . '">
  1278. </head>
  1279. <body>
  1280. <p>Redirecting to <a href="' . $url . '">' . $url . '</a></p>
  1281. </body>
  1282. </html>
  1283. ';
  1284. debug('Refresh: ' . $url);
  1285. exit(0);
  1286. }
  1287. /**
  1288. * Implement binary x_or
  1289. * @param string $a
  1290. * @param string $b
  1291. * @return string
  1292. */
  1293. function x_or ($a, $b) {
  1294. $r = "";
  1295. for ($i = 0; $i < strlen($b); $i++)
  1296. $r .= $a[$i] ^ $b[$i];
  1297. debug("Xor size: " . strlen($r));
  1298. return $r;
  1299. }
  1300. /*
  1301. * App Initialization
  1302. */
  1303. // Determine the charset to use
  1304. $GLOBALS['charset'] = 'iso-8859-1';
  1305. // Set the internal encoding
  1306. if (function_exists('mb_internal_encoding'))
  1307. mb_internal_encoding($charset);
  1308. // Avoid problems with non-default arg_separator.output settings
  1309. // Credit for this goes to user 'prelog' on the forums
  1310. ini_set('arg_separator.output', '&');
  1311. // Do a check to be sure everything is set up correctly
  1312. self_check();
  1313. function lxa_logged_in() {
  1314. global $wgAuth, $smf_settings, $smf_map;
  1315. $ID_MEMBER = 0;
  1316. if (isset($_COOKIE[$smf_settings['cookiename']]))
  1317. {
  1318. $_COOKIE[$smf_settings['cookiename']] = stripslashes($_COOKIE[$smf_settings['cookiename']]);
  1319. // Fix a security hole in PHP 4.3.9 and below...
  1320. if (preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~', $_COOKIE[$smf_settings['cookiename']]) == 1)
  1321. {
  1322. list ($ID_MEMBER, $password) = @unserialize($_COOKIE[$smf_settings['cookiename']]);
  1323. $ID_MEMBER = !empty($ID_MEMBER) && strlen($password) > 0 ? (int) $ID_MEMBER : 0;
  1324. }
  1325. }
  1326. // Only load this stuff if the user isn't a guest.
  1327. if ($ID_MEMBER != 0)
  1328. {
  1329. $conn = $wgAuth->connect();
  1330. $request = $wgAuth->query("
  1331. SELECT $smf_map[id_member], $smf_map[member_name], $smf_map[email_address], $smf_map[real_name],
  1332. $smf_map[is_activated], $smf_map[passwd], $smf_map[password_salt]
  1333. FROM $smf_settings[db_prefix]members
  1334. WHERE $smf_map[id_member] = '{$ID_MEMBER}'
  1335. LIMIT 1", $conn);
  1336. $user_settings = mysql_fetch_assoc($request);
  1337. // Did we find 'im? If not, junk it.
  1338. if (mysql_num_rows($request) != 0)
  1339. {
  1340. // SHA-1 passwords should be 40 characters long.
  1341. if (strlen($password) == 40)
  1342. $check = sha1($user_settings[$smf_map['passwd']] . $user_settings[$smf_map['password_salt']]) == $password;
  1343. else
  1344. $check = false;
  1345. // Wrong password or not activated - either way, you're going nowhere.
  1346. $ID_MEMBER = $check && ($user_settings[$smf_map['is_activated']] == 1 || $user_settings[$smf_map['is_activated']] == 11) ? $user_settings[$smf_map['id_member']] : 0;
  1347. }
  1348. else
  1349. $ID_MEMBER = 0;
  1350. mysql_free_result($request);
  1351. }
  1352. // Log out guests or members with invalid cookie passwords.
  1353. $lxa_logged_in = $ID_MEMBER != 0;
  1354. return $lxa_logged_in ? $user_settings[$smf_map['member_name']] : null;
  1355. }
  1356. $GLOBALS["lxa_logged_in"] = lxa_logged_in();
  1357. /**
  1358. * Determine the HTTP request port
  1359. * @name $port
  1360. * @global integer $GLOBALS['port']
  1361. */
  1362. $GLOBALS['port'] = ((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' && $_SERVER['SERVER_PORT'] == 443)
  1363. || $_SERVER['SERVER_PORT'] == 80)
  1364. ? ''
  1365. : ':' . $_SERVER['SERVER_PORT'];
  1366. /**
  1367. * Determine the HTTP request protocol
  1368. * @name $proto
  1369. * @global string $GLOBALS['proto']
  1370. */
  1371. $GLOBALS['proto'] = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
  1372. // Set the authorization state - DO NOT OVERRIDE
  1373. $profile['authorized'] = false;
  1374. // Determine the requested URL - DO NOT OVERRIDE
  1375. $profile['req_url'] = sprintf("%s://%s%s%s",
  1376. $proto,
  1377. $_SERVER['HTTP_HOST'],
  1378. $port,
  1379. $_SERVER["REQUEST_URI"]);
  1380. $baseurl = explode("?", $profile['req_url']);
  1381. $baseurl = $baseurl[0];
  1382. // Set a default IDP URL
  1383. if (! array_key_exists('idp_url', $profile)) {
  1384. $profile['idp_url'] = $baseurl;
  1385. if($lxa_logged_in)
  1386. $profile["idp_url"] .= "?u=" . urlencode($lxa_logged_in);
  1387. else if(isset($_GET["id"]))
  1388. $profile["idp_url"] .= "?u=" . $_GET["u"];
  1389. }
  1390. // Set the default allowance for testing
  1391. if (! array_key_exists('allow_test', $profile))
  1392. $profile['allow_test'] = false;
  1393. // Set the default allowance for gmp
  1394. if (! array_key_exists('allow_gmp', $profile))
  1395. $profile['allow_gmp'] = false;
  1396. // Set the default force bigmath - BAD IDEA to override this
  1397. if (! array_key_exists('force_bigmath', $profile))
  1398. $profile['force_bigmath'] = false;
  1399. // Determine if GMP is usable
  1400. $profile['use_gmp'] = (extension_loaded('gmp') && $profile['allow_gmp']) ? true : false;
  1401. // Determine if I can perform big math functions
  1402. $profile['use_bigmath'] = (extension_loaded('bcmath') || $profile['use_gmp'] || $profile['force_bigmath']) ? true : false;
  1403. // Set a default authentication domain
  1404. if (! array_key_exists('auth_domain', $profile))
  1405. $profile['auth_domain'] = $profile['req_url'] . ' ' . $profile['idp_url'];
  1406. // Set a default authentication realm
  1407. if (! array_key_exists('auth_realm', $profile))
  1408. $profile['auth_realm'] = 'SMFmyID';
  1409. // Determine the realm for digest authentication - DO NOT OVERRIDE
  1410. $profile['php_realm'] = $profile['auth_realm'] . (ini_get('safe_mode') ? '-' . getmyuid() : '');
  1411. // Set a default lifetime - the lesser of GC and cache time
  1412. if (! array_key_exists('lifetime', $profile)) {
  1413. $sce = session_cache_expire() * 60;
  1414. $gcm = ini_get('session.gc_maxlifetime');
  1415. $profile['lifetime'] = $sce < $gcm ? $sce : $gcm;
  1416. }
  1417. // Set a default log file
  1418. if (! array_key_exists('logfile', $profile))
  1419. $profile['logfile'] = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $profile['auth_realm'] . '.debug.log';
  1420. /*
  1421. * Optional Initialization
  1422. */
  1423. // Setup optional headers
  1424. $profile['opt_headers'] = array();
  1425. // Determine if I should add microid stuff
  1426. if (array_key_exists('microid', $profile)) {
  1427. $hash = sha1($profile['idp_url']);
  1428. $values = is_array($profile['microid']) ? $profile['microid'] : array($profile['microid']);
  1429. foreach ($values as $microid) {
  1430. preg_match('/^([a-z]+)/i', $microid, $mtx);
  1431. $profile['opt_headers'][] = sprintf('<meta name="microid" content="%s+%s:sha1:%s" />', $mtx[1], $proto, sha1(sha1($microid) . $hash));
  1432. }
  1433. }
  1434. // Determine if I should add pavatar stuff
  1435. if (array_key_exists('pavatar', $profile))
  1436. $profile['opt_headers'][] = sprintf('<link rel="pavatar" href="%s" />', $profile['pavatar']);
  1437. /*
  1438. * Do it
  1439. */
  1440. // Decide which runmode, based on user request or default
  1441. $run_mode = (isset($_REQUEST['openid_mode'])
  1442. && in_array($_REQUEST['openid_mode'], $known['openid_modes']))
  1443. ? $_REQUEST['openid_mode']
  1444. : 'no';
  1445. // Run in the determined runmode
  1446. debug("Run mode: $run_mode at: " . time());
  1447. debug($_REQUEST, 'Request params');
  1448. debug("Remote " . $_SERVER["REMOTE_ADDR"] . ", User agent: " . $_SERVER["HTTP_USER_AGENT"]);
  1449. call_user_func($run_mode . '_mode');
  1450. ?>