annotate forum/Sources/Subs-OpenID.php @ 87:df86d318892b website

Link to SampleType doc
author Chris Cannam
date Mon, 10 Feb 2014 18:11:48 +0000
parents e3e11437ecea
children
rev   line source
Chris@76 1 <?php
Chris@76 2
Chris@76 3 /**
Chris@76 4 * Simple Machines Forum (SMF)
Chris@76 5 *
Chris@76 6 * @package SMF
Chris@76 7 * @author Simple Machines http://www.simplemachines.org
Chris@76 8 * @copyright 2011 Simple Machines
Chris@76 9 * @license http://www.simplemachines.org/about/smf/license.php BSD
Chris@76 10 *
Chris@76 11 * @version 2.0
Chris@76 12 */
Chris@76 13
Chris@76 14 if (!defined('SMF'))
Chris@76 15 die('Hacking attempt...');
Chris@76 16
Chris@76 17 /* This file handles all of the OpenID interfacing and communications.
Chris@76 18 void smf_openID_validate(string openid_url, bool allow_immediate_validation = true)
Chris@76 19 - openid_uri is the URI given by the user
Chris@76 20 - Validates the URI and changes it to a fully canonicalize URL
Chris@76 21 - Determines the IDP server and delegation
Chris@76 22 - optional array of fields to restore when validation complete.
Chris@76 23 - Redirects the user to the IDP for validation
Chris@76 24 */
Chris@76 25
Chris@76 26 function smf_openID_validate($openid_uri, $return = false, $save_fields = array(), $return_action = null)
Chris@76 27 {
Chris@76 28 global $sourcedir, $scripturl, $boardurl, $modSettings;
Chris@76 29
Chris@76 30 $openid_url = smf_openID_canonize($openid_uri);
Chris@76 31
Chris@76 32 $response_data = smf_openID_getServerInfo($openid_url);
Chris@76 33 if ($response_data === false)
Chris@76 34 return 'no_data';
Chris@76 35
Chris@76 36 if (($assoc = smf_openID_getAssociation($response_data['server'])) == null)
Chris@76 37 $assoc = smf_openID_makeAssociation($response_data['server']);
Chris@76 38
Chris@76 39 // Before we go wherever it is we are going, store the GET and POST data, because it might be useful when we get back.
Chris@76 40 $request_time = time();
Chris@76 41 // Just in case they are doing something else at this time.
Chris@76 42 while (isset($_SESSION['openid']['saved_data'][$request_time]))
Chris@76 43 $request_time = md5($request_time);
Chris@76 44
Chris@76 45 $_SESSION['openid']['saved_data'][$request_time] = array(
Chris@76 46 'get' => $_GET,
Chris@76 47 'post' => $_POST,
Chris@76 48 'openid_uri' => $openid_url,
Chris@76 49 'cookieTime' => $modSettings['cookieTime'],
Chris@76 50 );
Chris@76 51
Chris@76 52 $parameters = array(
Chris@76 53 'openid.mode=checkid_setup',
Chris@76 54 'openid.trust_root=' . urlencode($scripturl),
Chris@76 55 'openid.identity=' . urlencode(empty($response_data['delegate']) ? $openid_url : $response_data['delegate']),
Chris@76 56 'openid.assoc_handle=' . urlencode($assoc['handle']),
Chris@76 57 'openid.return_to=' . urlencode($scripturl . '?action=openidreturn&sa=' . (!empty($return_action) ? $return_action : $_REQUEST['action']) . '&t=' . $request_time . (!empty($save_fields) ? '&sf=' . base64_encode(serialize($save_fields)) : '')),
Chris@76 58 );
Chris@76 59
Chris@76 60 // If they are logging in but don't yet have an account or they are registering, let's request some additional information
Chris@76 61 if (($_REQUEST['action'] == 'login2' && !smf_openid_member_exists($openid_url)) || ($_REQUEST['action'] == 'register' || $_REQUEST['action'] == 'register2'))
Chris@76 62 {
Chris@76 63 // Email is required.
Chris@76 64 $parameters[] = 'openid.sreg.required=email';
Chris@76 65 // The rest is just optional.
Chris@76 66 $parameters[] = 'openid.sreg.optional=nickname,dob,gender';
Chris@76 67 }
Chris@76 68
Chris@76 69 $redir_url = $response_data['server'] . '?' . implode('&', $parameters);
Chris@76 70
Chris@76 71 if ($return)
Chris@76 72 return $redir_url;
Chris@76 73 else
Chris@76 74 redirectexit($redir_url);
Chris@76 75 }
Chris@76 76
Chris@76 77 // Revalidate a user using OpenID. Note that this function will not return when authentication is required.
Chris@76 78 function smf_openID_revalidate()
Chris@76 79 {
Chris@76 80 global $user_settings;
Chris@76 81
Chris@76 82 if (isset($_SESSION['openid_revalidate_time']) && $_SESSION['openid_revalidate_time'] > time() - 60)
Chris@76 83 {
Chris@76 84 unset($_SESSION['openid_revalidate_time']);
Chris@76 85 return true;
Chris@76 86 }
Chris@76 87 else
Chris@76 88 smf_openID_validate($user_settings['openid_uri'], false, null, 'revalidate');
Chris@76 89
Chris@76 90 // We shouldn't get here.
Chris@76 91 trigger_error('Hacking attempt...', E_USER_ERROR);
Chris@76 92 }
Chris@76 93
Chris@76 94 function smf_openID_getAssociation($server, $handle = null, $no_delete = false)
Chris@76 95 {
Chris@76 96 global $smcFunc;
Chris@76 97
Chris@76 98 if (!$no_delete)
Chris@76 99 {
Chris@76 100 // Delete the already expired associations.
Chris@76 101 $smcFunc['db_query']('openid_delete_assoc_old', '
Chris@76 102 DELETE FROM {db_prefix}openid_assoc
Chris@76 103 WHERE expires <= {int:current_time}',
Chris@76 104 array(
Chris@76 105 'current_time' => time(),
Chris@76 106 )
Chris@76 107 );
Chris@76 108 }
Chris@76 109
Chris@76 110 // Get the association that has the longest lifetime from now.
Chris@76 111 $request = $smcFunc['db_query']('openid_select_assoc', '
Chris@76 112 SELECT server_url, handle, secret, issued, expires, assoc_type
Chris@76 113 FROM {db_prefix}openid_assoc
Chris@76 114 WHERE server_url = {string:server_url}' . ($handle === null ? '' : '
Chris@76 115 AND handle = {string:handle}') . '
Chris@76 116 ORDER BY expires DESC',
Chris@76 117 array(
Chris@76 118 'server_url' => $server,
Chris@76 119 'handle' => $handle,
Chris@76 120 )
Chris@76 121 );
Chris@76 122
Chris@76 123 if ($smcFunc['db_num_rows']($request) == 0)
Chris@76 124 return null;
Chris@76 125
Chris@76 126 $return = $smcFunc['db_fetch_assoc']($request);
Chris@76 127 $smcFunc['db_free_result']($request);
Chris@76 128
Chris@76 129 return $return;
Chris@76 130 }
Chris@76 131
Chris@76 132 function smf_openID_makeAssociation($server)
Chris@76 133 {
Chris@76 134 global $smcFunc, $modSettings, $p;
Chris@76 135
Chris@76 136 $parameters = array(
Chris@76 137 'openid.mode=associate',
Chris@76 138 );
Chris@76 139
Chris@76 140 // We'll need to get our keys for the Diffie-Hellman key exchange.
Chris@76 141 $dh_keys = smf_openID_setup_DH();
Chris@76 142
Chris@76 143 // If we don't support DH we'll have to see if the provider will accept no encryption.
Chris@76 144 if ($dh_keys === false)
Chris@76 145 $parameters[] = 'openid.session_type=';
Chris@76 146 else
Chris@76 147 {
Chris@76 148 $parameters[] = 'openid.session_type=DH-SHA1';
Chris@76 149 $parameters[] = 'openid.dh_consumer_public=' . urlencode(base64_encode(long_to_binary($dh_keys['public'])));
Chris@76 150 $parameters[] = 'openid.assoc_type=HMAC-SHA1';
Chris@76 151 }
Chris@76 152
Chris@76 153 // The data to post to the server.
Chris@76 154 $post_data = implode('&', $parameters);
Chris@76 155 $data = fetch_web_data($server, $post_data);
Chris@76 156
Chris@76 157 // Parse the data given.
Chris@76 158 preg_match_all('~^([^:]+):(.+)$~m', $data, $matches);
Chris@76 159 $assoc_data = array();
Chris@76 160
Chris@76 161 foreach ($matches[1] as $key => $match)
Chris@76 162 $assoc_data[$match] = $matches[2][$key];
Chris@76 163
Chris@76 164 if (!isset($assoc_data['assoc_type']) || (empty($assoc_data['mac_key']) && empty($assoc_data['enc_mac_key'])))
Chris@76 165 fatal_lang_error('openid_server_bad_response');
Chris@76 166
Chris@76 167 // Clean things up a bit.
Chris@76 168 $handle = isset($assoc_data['assoc_handle']) ? $assoc_data['assoc_handle'] : '';
Chris@76 169 $issued = time();
Chris@76 170 $expires = $issued + min((int)$assoc_data['expires_in'], 60);
Chris@76 171 $assoc_type = isset($assoc_data['assoc_type']) ? $assoc_data['assoc_type'] : '';
Chris@76 172
Chris@76 173 // !!! Is this really needed?
Chris@76 174 foreach (array('dh_server_public', 'enc_mac_key') as $key)
Chris@76 175 if (isset($assoc_data[$key]))
Chris@76 176 $assoc_data[$key] = str_replace(' ', '+', $assoc_data[$key]);
Chris@76 177
Chris@76 178 // Figure out the Diffie-Hellman secret.
Chris@76 179 if (!empty($assoc_data['enc_mac_key']))
Chris@76 180 {
Chris@76 181 $dh_secret = bcpowmod(binary_to_long(base64_decode($assoc_data['dh_server_public'])), $dh_keys['private'], $p);
Chris@76 182 $secret = base64_encode(binary_xor(sha1_raw(long_to_binary($dh_secret)), base64_decode($assoc_data['enc_mac_key'])));
Chris@76 183 }
Chris@76 184 else
Chris@76 185 $secret = $assoc_data['mac_key'];
Chris@76 186
Chris@76 187 // Store the data
Chris@76 188 $smcFunc['db_insert']('replace',
Chris@76 189 '{db_prefix}openid_assoc',
Chris@76 190 array('server_url' => 'string', 'handle' => 'string', 'secret' => 'string', 'issued' => 'int', 'expires' => 'int', 'assoc_type' => 'string'),
Chris@76 191 array($server, $handle, $secret, $issued, $expires, $assoc_type),
Chris@76 192 array('server_url', 'handle')
Chris@76 193 );
Chris@76 194
Chris@76 195 return array(
Chris@76 196 'server' => $server,
Chris@76 197 'handle' => $assoc_data['assoc_handle'],
Chris@76 198 'secret' => $secret,
Chris@76 199 'issued' => $issued,
Chris@76 200 'expires' => $expires,
Chris@76 201 'assoc_type' => $assoc_data['assoc_type'],
Chris@76 202 );
Chris@76 203 }
Chris@76 204
Chris@76 205 function smf_openID_removeAssociation($handle)
Chris@76 206 {
Chris@76 207 global $smcFunc;
Chris@76 208
Chris@76 209 $smcFunc['db_query']('openid_remove_association', '
Chris@76 210 DELETE FROM {db_prefix}openid_assoc
Chris@76 211 WHERE handle = {string:handle}',
Chris@76 212 array(
Chris@76 213 'handle' => $handle,
Chris@76 214 )
Chris@76 215 );
Chris@76 216 }
Chris@76 217
Chris@76 218 function smf_openID_return()
Chris@76 219 {
Chris@76 220 global $smcFunc, $user_info, $user_profile, $sourcedir, $modSettings, $context, $sc, $user_settings;
Chris@76 221
Chris@76 222 // Is OpenID even enabled?
Chris@76 223 if (empty($modSettings['enableOpenID']))
Chris@76 224 fatal_lang_error('no_access', false);
Chris@76 225
Chris@76 226 if (!isset($_GET['openid_mode']))
Chris@76 227 fatal_lang_error('openid_return_no_mode', false);
Chris@76 228
Chris@76 229 // !!! Check for error status!
Chris@76 230 if ($_GET['openid_mode'] != 'id_res')
Chris@76 231 fatal_lang_error('openid_not_resolved');
Chris@76 232
Chris@76 233 // SMF has this annoying habit of removing the + from the base64 encoding. So lets put them back.
Chris@76 234 foreach (array('openid_assoc_handle', 'openid_invalidate_handle', 'openid_sig', 'sf') as $key)
Chris@76 235 if (isset($_GET[$key]))
Chris@76 236 $_GET[$key] = str_replace(' ', '+', $_GET[$key]);
Chris@76 237
Chris@76 238 // Did they tell us to remove any associations?
Chris@76 239 if (!empty($_GET['openid_invalidate_handle']))
Chris@76 240 smf_openid_removeAssociation($_GET['openid_invalidate_handle']);
Chris@76 241
Chris@76 242 $server_info = smf_openid_getServerInfo($_GET['openid_identity']);
Chris@76 243
Chris@76 244 // Get the association data.
Chris@76 245 $assoc = smf_openID_getAssociation($server_info['server'], $_GET['openid_assoc_handle'], true);
Chris@76 246 if ($assoc === null)
Chris@76 247 fatal_lang_error('openid_no_assoc');
Chris@76 248
Chris@76 249 $secret = base64_decode($assoc['secret']);
Chris@76 250
Chris@76 251 $signed = explode(',', $_GET['openid_signed']);
Chris@76 252 $verify_str = '';
Chris@76 253 foreach ($signed as $sign)
Chris@76 254 {
Chris@76 255 $verify_str .= $sign . ':' . strtr($_GET['openid_' . str_replace('.', '_', $sign)], array('&amp;' => '&')) . "\n";
Chris@76 256 }
Chris@76 257
Chris@76 258 $verify_str = base64_encode(sha1_hmac($verify_str, $secret));
Chris@76 259
Chris@76 260 if ($verify_str != $_GET['openid_sig'])
Chris@76 261 {
Chris@76 262 fatal_lang_error('openid_sig_invalid', 'critical');
Chris@76 263 }
Chris@76 264
Chris@76 265 if (!isset($_SESSION['openid']['saved_data'][$_GET['t']]))
Chris@76 266 fatal_lang_error('openid_load_data');
Chris@76 267
Chris@76 268 $openid_uri = $_SESSION['openid']['saved_data'][$_GET['t']]['openid_uri'];
Chris@76 269 $modSettings['cookieTime'] = $_SESSION['openid']['saved_data'][$_GET['t']]['cookieTime'];
Chris@76 270
Chris@76 271 if (empty($openid_uri))
Chris@76 272 fatal_lang_error('openid_load_data');
Chris@76 273
Chris@76 274 // Any save fields to restore?
Chris@76 275 $context['openid_save_fields'] = isset($_GET['sf']) ? unserialize(base64_decode($_GET['sf'])) : array();
Chris@76 276
Chris@76 277 // Is there a user with this OpenID_uri?
Chris@76 278 $result = $smcFunc['db_query']('', '
Chris@76 279 SELECT passwd, id_member, id_group, lngfile, is_activated, email_address, additional_groups, member_name, password_salt,
Chris@76 280 openid_uri
Chris@76 281 FROM {db_prefix}members
Chris@76 282 WHERE openid_uri = {string:openid_uri}',
Chris@76 283 array(
Chris@76 284 'openid_uri' => $openid_uri,
Chris@76 285 )
Chris@76 286 );
Chris@76 287
Chris@76 288 $member_found = $smcFunc['db_num_rows']($result);
Chris@76 289
Chris@76 290 if (!$member_found && isset($_GET['sa']) && $_GET['sa'] == 'change_uri' && !empty($_SESSION['new_openid_uri']) && $_SESSION['new_openid_uri'] == $openid_uri)
Chris@76 291 {
Chris@76 292 // Update the member.
Chris@76 293 updateMemberData($user_settings['id_member'], array('openid_uri' => $openid_uri));
Chris@76 294
Chris@76 295 unset($_SESSION['new_openid_uri']);
Chris@76 296 $_SESSION['openid'] = array(
Chris@76 297 'verified' => true,
Chris@76 298 'openid_uri' => $openid_uri,
Chris@76 299 );
Chris@76 300
Chris@76 301 // Send them back to profile.
Chris@76 302 redirectexit('action=profile;area=authentication;updated');
Chris@76 303 }
Chris@76 304 elseif (!$member_found)
Chris@76 305 {
Chris@76 306 // Store the received openid info for the user when returned to the registration page.
Chris@76 307 $_SESSION['openid'] = array(
Chris@76 308 'verified' => true,
Chris@76 309 'openid_uri' => $openid_uri,
Chris@76 310 );
Chris@76 311 if (isset($_GET['openid_sreg_nickname']))
Chris@76 312 $_SESSION['openid']['nickname'] = $_GET['openid_sreg_nickname'];
Chris@76 313 if (isset($_GET['openid_sreg_email']))
Chris@76 314 $_SESSION['openid']['email'] = $_GET['openid_sreg_email'];
Chris@76 315 if (isset($_GET['openid_sreg_dob']))
Chris@76 316 $_SESSION['openid']['dob'] = $_GET['openid_sreg_dob'];
Chris@76 317 if (isset($_GET['openid_sreg_gender']))
Chris@76 318 $_SESSION['openid']['gender'] = $_GET['openid_sreg_gender'];
Chris@76 319
Chris@76 320 // Were we just verifying the registration state?
Chris@76 321 if (isset($_GET['sa']) && $_GET['sa'] == 'register2')
Chris@76 322 {
Chris@76 323 require_once($sourcedir . '/Register.php');
Chris@76 324 return Register2(true);
Chris@76 325 }
Chris@76 326 else
Chris@76 327 redirectexit('action=register');
Chris@76 328 }
Chris@76 329 elseif (isset($_GET['sa']) && $_GET['sa'] == 'revalidate' && $user_settings['openid_uri'] == $openid_uri)
Chris@76 330 {
Chris@76 331 $_SESSION['openid_revalidate_time'] = time();
Chris@76 332
Chris@76 333 // Restore the get data.
Chris@76 334 require_once($sourcedir . '/Subs-Auth.php');
Chris@76 335 $_SESSION['openid']['saved_data'][$_GET['t']]['get']['openid_restore_post'] = $_GET['t'];
Chris@76 336 $query_string = construct_query_string($_SESSION['openid']['saved_data'][$_GET['t']]['get']);
Chris@76 337
Chris@76 338 redirectexit($query_string);
Chris@76 339 }
Chris@76 340 else
Chris@76 341 {
Chris@76 342 $user_settings = $smcFunc['db_fetch_assoc']($result);
Chris@76 343 $smcFunc['db_free_result']($result);
Chris@76 344
Chris@76 345 $user_settings['passwd'] = sha1(strtolower($user_settings['member_name']) . $secret);
Chris@76 346 $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4);
Chris@76 347
Chris@76 348 updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt']));
Chris@76 349
Chris@76 350 // Cleanup on Aisle 5.
Chris@76 351 $_SESSION['openid'] = array(
Chris@76 352 'verified' => true,
Chris@76 353 'openid_uri' => $openid_uri,
Chris@76 354 );
Chris@76 355
Chris@76 356 require_once($sourcedir . '/LogInOut.php');
Chris@76 357
Chris@76 358 if (!checkActivation())
Chris@76 359 return;
Chris@76 360
Chris@76 361 DoLogin();
Chris@76 362 }
Chris@76 363 }
Chris@76 364
Chris@76 365 function smf_openID_canonize($uri)
Chris@76 366 {
Chris@76 367 // !!! Add in discovery.
Chris@76 368
Chris@76 369 if (strpos($uri, 'http://') !== 0 && strpos($uri, 'https://') !== 0)
Chris@76 370 $uri = 'http://' . $uri;
Chris@76 371
Chris@76 372 if (strpos(substr($uri, strpos($uri, '://') + 3), '/') === false)
Chris@76 373 $uri .= '/';
Chris@76 374
Chris@76 375 return $uri;
Chris@76 376 }
Chris@76 377
Chris@76 378 function smf_openid_member_exists($url)
Chris@76 379 {
Chris@76 380 global $smcFunc;
Chris@76 381
Chris@76 382 $request = $smcFunc['db_query']('openid_member_exists', '
Chris@76 383 SELECT mem.id_member, mem.member_name
Chris@76 384 FROM {db_prefix}members AS mem
Chris@76 385 WHERE mem.openid_uri = {string:openid_uri}',
Chris@76 386 array(
Chris@76 387 'openid_uri' => $url,
Chris@76 388 )
Chris@76 389 );
Chris@76 390 $member = $smcFunc['db_fetch_assoc']($request);
Chris@76 391 $smcFunc['db_free_result']($request);
Chris@76 392
Chris@76 393 return $member;
Chris@76 394 }
Chris@76 395
Chris@76 396 // Prepare for a Diffie-Hellman key exchange.
Chris@76 397 function smf_openID_setup_DH($regenerate = false)
Chris@76 398 {
Chris@76 399 global $p, $g;
Chris@76 400
Chris@76 401 // First off, do we have BC Math available?
Chris@76 402 if (!function_exists('bcpow'))
Chris@76 403 return false;
Chris@76 404
Chris@76 405 // Defined in OpenID spec.
Chris@76 406 $p = '155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443';
Chris@76 407 $g = '2';
Chris@76 408
Chris@76 409 // Make sure the scale is set.
Chris@76 410 bcscale(0);
Chris@76 411
Chris@76 412 return smf_openID_get_keys($regenerate);
Chris@76 413 }
Chris@76 414
Chris@76 415 function smf_openID_get_keys($regenerate)
Chris@76 416 {
Chris@76 417 global $modSettings, $p, $g;
Chris@76 418
Chris@76 419 // Ok lets take the easy way out, are their any keys already defined for us? They are changed in the daily maintenance scheduled task.
Chris@76 420 if (!empty($modSettings['dh_keys']) && !$regenerate)
Chris@76 421 {
Chris@76 422 // Sweeeet!
Chris@76 423 list ($public, $private) = explode("\n", $modSettings['dh_keys']);
Chris@76 424 return array(
Chris@76 425 'public' => base64_decode($public),
Chris@76 426 'private' => base64_decode($private),
Chris@76 427 );
Chris@76 428 }
Chris@76 429
Chris@76 430 // Dang it, now I have to do math. And it's not just ordinary math, its the evil big interger math. This will take a few seconds.
Chris@76 431 $private = smf_openid_generate_private_key();
Chris@76 432 $public = bcpowmod($g, $private, $p);
Chris@76 433
Chris@76 434 // Now that we did all that work, lets save it so we don't have to keep doing it.
Chris@76 435 $keys = array('dh_keys' => base64_encode($public) . "\n" . base64_encode($private));
Chris@76 436 updateSettings($keys);
Chris@76 437
Chris@76 438 return array(
Chris@76 439 'public' => $public,
Chris@76 440 'private' => $private,
Chris@76 441 );
Chris@76 442 }
Chris@76 443
Chris@76 444 function smf_openid_generate_private_key()
Chris@76 445 {
Chris@76 446 global $p;
Chris@76 447 static $cache = array();
Chris@76 448
Chris@76 449 $byte_string = long_to_binary($p);
Chris@76 450
Chris@76 451 if (isset($cache[$byte_string]))
Chris@76 452 list ($dup, $num_bytes) = $cache[$byte_string];
Chris@76 453 else
Chris@76 454 {
Chris@76 455 $num_bytes = strlen($byte_string) - ($byte_string[0] == "\x00" ? 1 : 0);
Chris@76 456
Chris@76 457 $max_rand = bcpow(256, $num_bytes);
Chris@76 458
Chris@76 459 $dup = bcmod($max_rand, $num_bytes);
Chris@76 460
Chris@76 461 $cache[$byte_string] = array($dup, $num_bytes);
Chris@76 462 }
Chris@76 463
Chris@76 464 do
Chris@76 465 {
Chris@76 466 $str = '';
Chris@76 467 for ($i = 0; $i < $num_bytes; $i += 4)
Chris@76 468 $str .= pack('L', mt_rand());
Chris@76 469
Chris@76 470 $bytes = "\x00" . $str;
Chris@76 471
Chris@76 472 $num = binary_to_long($bytes);
Chris@76 473 } while (bccomp($num, $dup) < 0);
Chris@76 474
Chris@76 475 return bcadd(bcmod($num, $p), 1);
Chris@76 476 }
Chris@76 477
Chris@76 478 function smf_openID_getServerInfo($openid_url)
Chris@76 479 {
Chris@76 480 global $sourcedir;
Chris@76 481
Chris@76 482 require_once($sourcedir . '/Subs-Package.php');
Chris@76 483
Chris@76 484 // Get the html and parse it for the openid variable which will tell us where to go.
Chris@76 485 $webdata = fetch_web_data($openid_url);
Chris@76 486
Chris@76 487 if (empty($webdata))
Chris@76 488 return false;
Chris@76 489
Chris@76 490 $response_data = array();
Chris@76 491
Chris@76 492 // Some OpenID servers have strange but still valid HTML which makes our job hard.
Chris@76 493 if (preg_match_all('~<link([\s\S]*?)/?>~i', $webdata, $link_matches) == 0)
Chris@76 494 fatal_lang_error('openid_server_bad_response');
Chris@76 495
Chris@76 496 foreach ($link_matches[1] as $link_match)
Chris@76 497 {
Chris@76 498 if (preg_match('~rel="([\s\S]*?)"~i', $link_match, $rel_match) == 0 || preg_match('~href="([\s\S]*?)"~i', $link_match, $href_match) == 0)
Chris@76 499 continue;
Chris@76 500
Chris@76 501 $rels = preg_split('~\s+~', $rel_match[1]);
Chris@76 502 foreach ($rels as $rel)
Chris@76 503 if (preg_match('~openid2?\.(server|delegate|provider)~i', $rel, $match) != 0)
Chris@76 504 $response_data[$match[1]] = $href_match[1];
Chris@76 505 }
Chris@76 506
Chris@76 507 if (empty($response_data['server']))
Chris@76 508 if (empty($response_data['provider']))
Chris@76 509 fatal_lang_error('openid_server_bad_response');
Chris@76 510 else
Chris@76 511 $response_data['server'] = $response_data['provider'];
Chris@76 512
Chris@76 513 return $response_data;
Chris@76 514 }
Chris@76 515
Chris@76 516 function sha1_hmac($data, $key)
Chris@76 517 {
Chris@76 518
Chris@76 519 if (strlen($key) > 64)
Chris@76 520 $key = sha1_raw($key);
Chris@76 521
Chris@76 522 // Pad the key if need be.
Chris@76 523 $key = str_pad($key, 64, chr(0x00));
Chris@76 524 $ipad = str_repeat(chr(0x36), 64);
Chris@76 525 $opad = str_repeat(chr(0x5c), 64);
Chris@76 526 $hash1 = sha1_raw(($key ^ $ipad) . $data);
Chris@76 527 $hmac = sha1_raw(($key ^ $opad) . $hash1);
Chris@76 528 return $hmac;
Chris@76 529 }
Chris@76 530
Chris@76 531 function sha1_raw($text)
Chris@76 532 {
Chris@76 533 if (version_compare(PHP_VERSION, '5.0.0') >= 0)
Chris@76 534 return sha1($text, true);
Chris@76 535
Chris@76 536 $hex = sha1($text);
Chris@76 537 $raw = '';
Chris@76 538 for ($i = 0; $i < 40; $i += 2)
Chris@76 539 {
Chris@76 540 $hexcode = substr($hex, $i, 2);
Chris@76 541 $charcode = (int) base_convert($hexcode, 16, 10);
Chris@76 542 $raw .= chr($charcode);
Chris@76 543 }
Chris@76 544
Chris@76 545 return $raw;
Chris@76 546 }
Chris@76 547
Chris@76 548 function binary_to_long($str)
Chris@76 549 {
Chris@76 550 $bytes = array_merge(unpack('C*', $str));
Chris@76 551
Chris@76 552 $n = 0;
Chris@76 553
Chris@76 554 foreach ($bytes as $byte)
Chris@76 555 {
Chris@76 556 $n = bcmul($n, 256);
Chris@76 557 $n = bcadd($n, $byte);
Chris@76 558 }
Chris@76 559
Chris@76 560 return $n;
Chris@76 561 }
Chris@76 562
Chris@76 563 function long_to_binary($value)
Chris@76 564 {
Chris@76 565 $cmp = bccomp($value, 0);
Chris@76 566 if ($cmp < 0)
Chris@76 567 fatal_error('Only non-negative integers allowed.');
Chris@76 568
Chris@76 569 if ($cmp == 0)
Chris@76 570 return "\x00";
Chris@76 571
Chris@76 572 $bytes = array();
Chris@76 573
Chris@76 574 while (bccomp($value, 0) > 0)
Chris@76 575 {
Chris@76 576 array_unshift($bytes, bcmod($value, 256));
Chris@76 577 $value = bcdiv($value, 256);
Chris@76 578 }
Chris@76 579
Chris@76 580 if ($bytes && ($bytes[0] > 127))
Chris@76 581 array_unshift($bytes, 0);
Chris@76 582
Chris@76 583 $return = '';
Chris@76 584 foreach ($bytes as $byte)
Chris@76 585 $return .= pack('C', $byte);
Chris@76 586
Chris@76 587 return $return;
Chris@76 588 }
Chris@76 589
Chris@76 590 function binary_xor($num1, $num2)
Chris@76 591 {
Chris@76 592 $return = '';
Chris@76 593
Chris@76 594 for ($i = 0; $i < strlen($num2); $i++)
Chris@76 595 $return .= $num1[$i] ^ $num2[$i];
Chris@76 596
Chris@76 597 return $return;
Chris@76 598 }
Chris@76 599
Chris@76 600 // PHP 4 didn't have bcpowmod.
Chris@76 601 if (!function_exists('bcpowmod') && function_exists('bcpow'))
Chris@76 602 {
Chris@76 603 function bcpowmod($num1, $num2, $num3)
Chris@76 604 {
Chris@76 605 return bcmod(bcpow($num1, $num2), $num3);
Chris@76 606 }
Chris@76 607 }
Chris@76 608
Chris@76 609 ?>