1724 lines
43 KiB
PHP
1724 lines
43 KiB
PHP
<?php
|
|
// PLEASE DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING!
|
|
|
|
/**
|
|
* phpMyID - A standalone, single user, OpenID Identity Provider
|
|
*
|
|
* @package phpMyID
|
|
* @author CJ Niemira <siege (at) siege (dot) org>
|
|
* @copyright 2006-2008
|
|
* @license http://www.gnu.org/licenses/gpl.html GNU Public License
|
|
* @url http://siege.org/projects/phpMyID
|
|
* @version 0.9
|
|
*/
|
|
|
|
|
|
/**
|
|
* Set a constant to indicate that phpMyID is running
|
|
*/
|
|
define('PHPMYID_STARTED', true);
|
|
|
|
/**
|
|
* List the known types and modes
|
|
* @name $known
|
|
* @global array $GLOBALS['known']
|
|
*/
|
|
$GLOBALS['known'] = array(
|
|
'assoc_types' => array('HMAC-SHA1'),
|
|
|
|
'openid_modes' => array('accept',
|
|
'associate',
|
|
'authorize',
|
|
'cancel',
|
|
'checkid_immediate',
|
|
'checkid_setup',
|
|
'check_authentication',
|
|
'error',
|
|
'id_res',
|
|
'login',
|
|
'logout',
|
|
'test'),
|
|
|
|
'session_types' => array('',
|
|
'DH-SHA1'),
|
|
|
|
'bigmath_types' => array('DH-SHA1'),
|
|
);
|
|
|
|
/**
|
|
* Defined by OpenID spec
|
|
* @name $g
|
|
* @global integer $GLOBALS['g']
|
|
*/
|
|
$GLOBALS['g'] = 2;
|
|
|
|
/**
|
|
* Defined by OpenID spec
|
|
* @name $p
|
|
* @global integer $GLOBALS['p']
|
|
*/
|
|
$GLOBALS['p'] = '155172898181473697471232257763715539915724801966915404479707' .
|
|
'7953140576293785419175806512274236981889937278161526466314385615958256881888' .
|
|
'8995127215884267541995034125870655654980358010487053768147672651325574704076' .
|
|
'5857479291291572334510643245094715007229621094194349783925984760375594985848' .
|
|
'253359305585439638443';
|
|
|
|
|
|
// Runmode functions
|
|
|
|
/**
|
|
* Allow the user to accept trust on a URL
|
|
* @global array $profile
|
|
*/
|
|
function accept_mode () {
|
|
global $profile;
|
|
|
|
// this is a user session
|
|
user_session();
|
|
|
|
// the user needs refresh urls in their session to access this mode
|
|
if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_url']))
|
|
error_500('You may not access this mode directly.');
|
|
|
|
// has the user accepted the trust_root?
|
|
$accepted = @strlen($_REQUEST['accepted'])
|
|
? $_REQUEST['accepted']
|
|
: null;
|
|
|
|
// if so, refresh back to post_accept_url
|
|
if ($accepted === 'yes') {
|
|
$_SESSION['accepted_url'] = $_SESSION['unaccepted_url'];
|
|
wrap_redirect($_SESSION['post_accept_url']);
|
|
|
|
// if they rejected it, return to the client
|
|
} elseif ($accepted === 'no') {
|
|
wrap_redirect($_SESSION['cancel_accept_url']);
|
|
}
|
|
|
|
// if neither, offer the trust request
|
|
$q = strpos($profile['req_url'], '?') ? '&' : '?';
|
|
$yes = $profile['req_url'] . $q . 'accepted=yes';
|
|
$no = $profile['req_url'] . $q . 'accepted=no';
|
|
|
|
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>');
|
|
}
|
|
|
|
/** * Perform an association with a consumer
|
|
* @global array $known
|
|
* @global array $profile
|
|
* @global integer $g
|
|
* @global integer $p
|
|
*/
|
|
function associate_mode () {
|
|
global $g, $known, $p, $profile;
|
|
|
|
// Validate the request
|
|
if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'associate')
|
|
error_400();
|
|
|
|
// Get the request options, using defaults as necessary
|
|
$assoc_type = (@strlen($_REQUEST['openid_assoc_type'])
|
|
&& in_array($_REQUEST['openid_assoc_type'], $known['assoc_types']))
|
|
? $_REQUEST['openid_assoc_type']
|
|
: 'HMAC-SHA1';
|
|
|
|
$session_type = (@strlen($_REQUEST['openid_session_type'])
|
|
&& in_array($_REQUEST['openid_session_type'], $known['session_types']))
|
|
? $_REQUEST['openid_session_type']
|
|
: '';
|
|
|
|
$dh_modulus = (@strlen($_REQUEST['openid_dh_modulus']))
|
|
? long(base64_decode($_REQUEST['openid_dh_modulus']))
|
|
: ($session_type == 'DH-SHA1'
|
|
? $p
|
|
: null);
|
|
|
|
$dh_gen = (@strlen($_REQUEST['openid_dh_gen']))
|
|
? long(base64_decode($_REQUEST['openid_dh_gen']))
|
|
: ($session_type == 'DH-SHA1'
|
|
? $g
|
|
: null);
|
|
|
|
$dh_consumer_public = (@strlen($_REQUEST['openid_dh_consumer_public']))
|
|
? $_REQUEST['openid_dh_consumer_public']
|
|
: ($session_type == 'DH-SHA1'
|
|
? error_post('dh_consumer_public was not specified')
|
|
: null);
|
|
|
|
$lifetime = time() + $profile['lifetime'];
|
|
|
|
// Create standard keys
|
|
$keys = array(
|
|
'assoc_type' => $assoc_type,
|
|
'expires_in' => $profile['lifetime']
|
|
);
|
|
|
|
// If I can't handle bigmath, default to plaintext sessions
|
|
if (in_array($session_type, $known['bigmath_types']) && $profile['use_bigmath'] === false)
|
|
$session_type = null;
|
|
|
|
// Add response keys based on the session type
|
|
switch ($session_type) {
|
|
case 'DH-SHA1':
|
|
// Create the associate id and shared secret now
|
|
list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
|
|
|
|
// Compute the Diffie-Hellman stuff
|
|
$private_key = random($dh_modulus);
|
|
$public_key = bmpowmod($dh_gen, $private_key, $dh_modulus);
|
|
$remote_key = long(base64_decode($dh_consumer_public));
|
|
$ss = bmpowmod($remote_key, $private_key, $dh_modulus);
|
|
|
|
$keys['assoc_handle'] = $assoc_handle;
|
|
$keys['session_type'] = $session_type;
|
|
$keys['dh_server_public'] = base64_encode(bin($public_key));
|
|
$keys['enc_mac_key'] = base64_encode(x_or(sha1_20(bin($ss)), $shared_secret));
|
|
|
|
break;
|
|
|
|
default:
|
|
// Create the associate id and shared secret now
|
|
list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
|
|
|
|
$keys['assoc_handle'] = $assoc_handle;
|
|
$keys['mac_key'] = base64_encode($shared_secret);
|
|
}
|
|
|
|
// Return the keys
|
|
wrap_kv($keys);
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform a user authorization
|
|
* @global array $profile
|
|
*/
|
|
function authorize_mode () {
|
|
global $profile;
|
|
global $USERNAME;
|
|
|
|
// this is a user session
|
|
|
|
// the user needs refresh urls in their session to access this mode
|
|
if (! isset($_SESSION['post_auth_url']) || ! isset($_SESSION['cancel_auth_url']))
|
|
error_500('You may not access this mode directly.');
|
|
|
|
if (isset($_SERVER['PHP_AUTH_USER']) && $profile['authorized'] === false && $_SERVER['PHP_AUTH_USER']==$USERNAME) {
|
|
if (OC_USER::checkPassword($USERNAME, $_SERVER['PHP_AUTH_PW'])) {// successful login!
|
|
|
|
// return to the refresh url if they get in
|
|
$_SESSION['openid_auth']=true;
|
|
$_SESSION['openid_user']=$USERNAME;
|
|
wrap_redirect($_SESSION['post_auth_url']);
|
|
|
|
// failed login
|
|
} else {
|
|
$_SESSION['failures']++;
|
|
debug('Login failed');
|
|
debug('Fail count: ' . $_SESSION['failures']);
|
|
}
|
|
|
|
}
|
|
|
|
// if we get this far the user is not authorized, so send the headers
|
|
$uid = uniqid(mt_rand(1,9));
|
|
$_SESSION['uniqid'] = $uid;
|
|
|
|
// debug('Prompting user to log in. Stale? ' . $stale);
|
|
header('HTTP/1.0 401 Unauthorized');
|
|
// header(sprintf('WWW-Authenticate: Digest qop="auth-int, auth", realm="%s", domain="%s", nonce="%s", opaque="%s", stale="%s", algorithm="MD5"', $profile['auth_realm'], $profile['auth_domain'], $uid, md5($profile['auth_realm']), $stale ? 'true' : 'false'));
|
|
header('WWW-Authenticate: Basic realm="ownCloud"');
|
|
$q = strpos($_SESSION['cancel_auth_url'], '?') ? '&' : '?';
|
|
wrap_refresh($_SESSION['cancel_auth_url'] . $q . 'openid.mode=cancel');
|
|
// die('401 Unauthorized');
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a consumer's request for cancellation.
|
|
*/
|
|
function cancel_mode () {
|
|
wrap_html('Request cancelled.');
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a consumer's request to see if the user is authenticated
|
|
*/
|
|
function check_authentication_mode () {
|
|
// Validate the request
|
|
if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'check_authentication')
|
|
error_400();
|
|
|
|
$assoc_handle = @strlen($_REQUEST['openid_assoc_handle'])
|
|
? $_REQUEST['openid_assoc_handle']
|
|
: error_post('Missing assoc_handle');
|
|
|
|
$sig = @strlen($_REQUEST['openid_sig'])
|
|
? $_REQUEST['openid_sig']
|
|
: error_post('Missing sig');
|
|
|
|
$signed = @strlen($_REQUEST['openid_signed'])
|
|
? $_REQUEST['openid_signed']
|
|
: error_post('Missing signed');
|
|
|
|
// Prepare the return keys
|
|
$keys = array(
|
|
'openid.mode' => 'id_res'
|
|
);
|
|
|
|
// Invalidate the assoc handle if we need to
|
|
if (@strlen($_REQUEST['openid_invalidate_handle'])) {
|
|
destroy_assoc_handle($_REQUEST['openid_invalidate_handle']);
|
|
|
|
$keys['invalidate_handle'] = $_REQUEST['openid_invalidate_handle'];
|
|
}
|
|
|
|
// Validate the sig by recreating the kv pair and signing
|
|
$_REQUEST['openid_mode'] = 'id_res';
|
|
$tokens = '';
|
|
foreach (explode(',', $signed) as $param) {
|
|
$post = preg_replace('/\./', '_', $param);
|
|
$tokens .= sprintf("%s:%s\n", $param, $_REQUEST['openid_' . $post]);
|
|
}
|
|
|
|
// Add the sreg stuff, if we've got it
|
|
if (isset($sreg_required)) {
|
|
foreach (explode(',', $sreg_required) as $key) {
|
|
if (! isset($sreg[$key]))
|
|
continue;
|
|
$skey = 'sreg.' . $key;
|
|
|
|
$tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]);
|
|
$keys[$skey] = $sreg[$key];
|
|
$fields[] = $skey;
|
|
}
|
|
}
|
|
|
|
// Look up the consumer's shared_secret and timeout
|
|
list ($shared_secret, $expires) = secret($assoc_handle);
|
|
|
|
// if I can't verify the assoc_handle, or if it's expired
|
|
if ($shared_secret == false || (is_numeric($expires) && $expires < time())) {
|
|
$keys['is_valid'] = 'false';
|
|
|
|
} else {
|
|
$ok = base64_encode(hmac($shared_secret, $tokens));
|
|
$keys['is_valid'] = ($sig == $ok) ? 'true' : 'false';
|
|
}
|
|
|
|
// Return the keys
|
|
wrap_kv($keys);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a consumer's request to see if the end user is logged in
|
|
* @global array $known
|
|
* @global array $profile
|
|
* @global array $sreg
|
|
*/
|
|
function checkid ( $wait ) {
|
|
global $known, $profile, $sreg;
|
|
global $USERNAME;
|
|
|
|
// This is a user session
|
|
user_session();
|
|
|
|
// Get the options, use defaults as necessary
|
|
$return_to = @strlen($_REQUEST['openid_return_to'])
|
|
? $_REQUEST['openid_return_to']
|
|
: error_400('Missing return1_to');
|
|
|
|
$identity = @strlen($_REQUEST['openid_identity'])
|
|
? $_REQUEST['openid_identity']
|
|
: error_get($return_to, 'Missing identity');
|
|
|
|
$assoc_handle = @strlen($_REQUEST['openid_assoc_handle'])
|
|
? $_REQUEST['openid_assoc.handle']
|
|
: null;
|
|
|
|
$trust_root = @strlen($_REQUEST['openid_trust_root'])
|
|
? $_REQUEST['openid_trust_root']
|
|
: $return_to;
|
|
|
|
$sreg_required = @strlen($_REQUEST['openid_sreg_required'])
|
|
? $_REQUEST['openid_sreg.required']
|
|
: '';
|
|
|
|
$sreg_optional = @strlen($_REQUEST['openid_sreg_optional'])
|
|
? $_REQUEST['openid_sreg.optional']
|
|
: '';
|
|
|
|
// determine the cancel url
|
|
$q = strpos($return_to, '?') ? '&' : '?';
|
|
$cancel_url = $return_to . $q . 'openid.mode=cancel';
|
|
|
|
// required and optional make no difference to us
|
|
$sreg_required .= ',' . $sreg_optional;
|
|
// do the trust_root analysis
|
|
if ($trust_root != $return_to) {
|
|
// the urls are not the same, be sure return decends from trust
|
|
if (! url_descends($return_to, $trust_root))
|
|
error_500('Invalid trust_root: "' . $trust_root . '"');
|
|
|
|
}
|
|
|
|
// transfer the user to the url accept mode if they're paranoid
|
|
if ($wait == 1 && isset($profile['paranoid']) && $profile['paranoid'] === true && (! session_is_registered('accepted_url') || $_SESSION['accepted_url'] != $trust_root)) {
|
|
$_SESSION['cancel_accept_url'] = $cancel_url;
|
|
$_SESSION['post_accept_url'] = $profile['req_url'];
|
|
$_SESSION['unaccepted_url'] = $trust_root;
|
|
|
|
debug('Transferring to acceptance mode.');
|
|
debug('Cancel URL: ' . $_SESSION['cancel_accept_url']);
|
|
debug('Post URL: ' . $_SESSION['post_accept_url']);
|
|
|
|
$q = strpos($profile['idp_url'], '?') ? '&' : '?';
|
|
wrap_redirect($profile['idp_url'] . $q . 'openid.mode=accept');
|
|
}
|
|
|
|
// make sure i am this identifier
|
|
if ($identity != $profile['idp_url']) {
|
|
debug("Invalid identity: $identity");
|
|
debug("IdP URL: " . $profile['idp_url']);
|
|
error_get($return_to, "Invalid identity: '$identity'");
|
|
}
|
|
|
|
// begin setting up return keys
|
|
$keys = array(
|
|
'mode' => 'id_res'
|
|
);
|
|
|
|
// if the user is not logged in, transfer to the authorization mode
|
|
if ($_SESSION['openid_auth'] === false || $USERNAME != $_SESSION['openid_user']) {
|
|
// users can only be logged in to one url at a time
|
|
$_SESSION['auth_username'] = null;
|
|
$_SESSION['auth_url'] = null;
|
|
|
|
if ($wait) {
|
|
unset($_SESSION['uniqid']);
|
|
|
|
$_SESSION['cancel_auth_url'] = $cancel_url;
|
|
$_SESSION['post_auth_url'] = $profile['req_url'];
|
|
|
|
debug('Transferring to authorization mode.');
|
|
debug('Cancel URL: ' . $_SESSION['cancel_auth_url']);
|
|
debug('Post URL: ' . $_SESSION['post_auth_url']);
|
|
|
|
$q = strpos($profile['idp_url'], '?') ? '&' : '?';
|
|
wrap_redirect($profile['idp_url'] . $q . 'openid.mode=authorize');
|
|
} else {
|
|
$keys['user_setup_url'] = $profile['idp_url'];
|
|
}
|
|
|
|
// the user is logged in
|
|
} else {
|
|
// remove the refresh URLs if set
|
|
unset($_SESSION['cancel_auth_url']);
|
|
unset($_SESSION['post_auth_url']);
|
|
|
|
// check the assoc handle
|
|
list($shared_secret, $expires) = secret($assoc_handle);
|
|
|
|
// if I can't verify the assoc_handle, or if it's expired
|
|
if ($shared_secret == false || (is_numeric($expires) && $expires < time())) {
|
|
debug("Session expired or missing key: $expires < " . time());
|
|
if ($assoc_handle != null) {
|
|
$keys['invalidate_handle'] = $assoc_handle;
|
|
destroy_assoc_handle($assoc_handle);
|
|
}
|
|
|
|
$lifetime = time() + $profile['lifetime'];
|
|
list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
|
|
}
|
|
|
|
$keys['identity'] = $profile['idp_url'];
|
|
$keys['assoc_handle'] = $assoc_handle;
|
|
$keys['return_to'] = $return_to;
|
|
|
|
$fields = array_keys($keys);
|
|
$tokens = '';
|
|
foreach ($fields as $key)
|
|
$tokens .= sprintf("%s:%s\n", $key, $keys[$key]);
|
|
|
|
// add sreg keys
|
|
foreach (explode(',', $sreg_required) as $key) {
|
|
if (! isset($sreg[$key]))
|
|
continue;
|
|
$skey = 'sreg.' . $key;
|
|
|
|
$tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]);
|
|
$keys[$skey] = $sreg[$key];
|
|
$fields[] = $skey;
|
|
}
|
|
|
|
$keys['signed'] = implode(',', $fields);
|
|
$keys['sig'] = base64_encode(hmac($shared_secret, $tokens));
|
|
}
|
|
|
|
wrap_keyed_redirect($return_to, $keys);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a consumer's request to see if the user is already logged in
|
|
*/
|
|
function checkid_immediate_mode () {
|
|
if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_immediate')
|
|
error_500();
|
|
|
|
checkid(false);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle a consumer's request to see if the user is logged in, but be willing
|
|
* to wait for them to perform a login if they're not
|
|
*/
|
|
function checkid_setup_mode () {
|
|
if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_setup')
|
|
error_500();
|
|
|
|
checkid(true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle errors
|
|
*/
|
|
function error_mode () {
|
|
isset($_REQUEST['openid_error'])
|
|
? wrap_html($_REQUEST['openid_error'])
|
|
: error_500();
|
|
}
|
|
|
|
|
|
/**
|
|
* Show a user if they are logged in or not
|
|
* @global array $profile
|
|
*/
|
|
function id_res_mode () {
|
|
global $profile;
|
|
|
|
user_session();
|
|
|
|
if ($profile['authorized'])
|
|
wrap_html('You are logged in as ' . $_SESSION['auth_username']);
|
|
|
|
wrap_html('You are not logged in');
|
|
}
|
|
|
|
|
|
/**
|
|
* Allow a user to perform a static login
|
|
* @global array $profile
|
|
*/
|
|
function login_mode () {
|
|
global $profile;
|
|
|
|
user_session();
|
|
|
|
if ($profile['authorized'])
|
|
id_res_mode();
|
|
|
|
$keys = array(
|
|
'mode' => 'checkid_setup',
|
|
'identity' => $profile['idp_url'],
|
|
'return_to' => $profile['idp_url']
|
|
);
|
|
|
|
wrap_keyed_redirect($profile['idp_url'], $keys);
|
|
}
|
|
|
|
|
|
/**
|
|
* Allow a user to perform a static logout
|
|
* @global array $profile
|
|
*/
|
|
function logout_mode () {
|
|
global $profile;
|
|
|
|
user_session();
|
|
|
|
if (! $profile['authorized'])
|
|
wrap_html('You were not logged in');
|
|
|
|
$_SESSION = array();
|
|
session_destroy();
|
|
debug('User session destroyed.');
|
|
|
|
header('HTTP/1.0 401 Unauthorized');
|
|
wrap_redirect($profile['idp_url']);
|
|
}
|
|
|
|
|
|
/**
|
|
* The default information screen
|
|
* @global array $profile
|
|
*/
|
|
function no_mode () {
|
|
global $profile;
|
|
|
|
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));
|
|
}
|
|
|
|
|
|
/**
|
|
* Testing for setup
|
|
* @global array $profile
|
|
*/
|
|
function test_mode () {
|
|
global $profile, $p, $g;
|
|
|
|
if ($profile['allow_test'] != true)
|
|
error_403();
|
|
|
|
@ini_set('max_execution_time', 180);
|
|
|
|
$test_expire = time() + 120;
|
|
$test_ss_enc = 'W7hvmld2yEYdDb0fHfSkKhQX+PM=';
|
|
$test_ss = base64_decode($test_ss_enc);
|
|
$test_token = "alpha:bravo\ncharlie:delta\necho:foxtrot";
|
|
$test_server_private = '11263846781670293092494395517924811173145217135753406847875706165886322533899689335716152496005807017390233667003995430954419468996805220211293016296351031812246187748601293733816011832462964410766956326501185504714561648498549481477143603650090931135412673422192550825523386522507656442905243832471167330268';
|
|
$test_client_public = base64_decode('AL63zqI5a5p8HdXZF5hFu8p+P9GOb816HcHuvNOhqrgkKdA3fO4XEzmldlb37nv3+xqMBgWj6gxT7vfuFerEZLBvuWyVvR7IOGZmx0BAByoq3fxYd3Fpe2Coxngs015vK37otmH8e83YyyGo5Qua/NAf13yz1PVuJ5Ctk7E+YdVc');
|
|
|
|
$res = array();
|
|
|
|
// bcmath
|
|
$res['bcmath'] = extension_loaded('bcmath')
|
|
? 'pass' : 'warn - not loaded';
|
|
|
|
// gmp
|
|
if ($profile['allow_gmp']) {
|
|
$res['gmp'] = extension_loaded('gmp')
|
|
? 'pass' : 'warn - not loaded';
|
|
} else {
|
|
$res['gmp'] = 'pass - n/a';
|
|
}
|
|
|
|
// sys_get_temp_dir
|
|
$res['logfile'] = is_writable($profile['logfile'])
|
|
? 'pass' : "warn - log is not writable";
|
|
|
|
// session & new_assoc
|
|
user_session();
|
|
list($test_assoc, $test_new_ss) = new_assoc($test_expire);
|
|
$res['session'] = ($test_assoc != session_id())
|
|
? 'pass' : 'fail';
|
|
|
|
// secret
|
|
@session_unregister('shared_secret');
|
|
list($check, $check2) = secret($test_assoc);
|
|
$res['secret'] = ($check == $test_new_ss)
|
|
? 'pass' : 'fail';
|
|
|
|
// expire
|
|
$res['expire'] = ($check2 <= $test_expire)
|
|
? 'pass' : 'fail';
|
|
|
|
// base64
|
|
$res['base64'] = (base64_encode($test_ss) == $test_ss_enc)
|
|
? 'pass' : 'fail';
|
|
|
|
// hmac
|
|
$test_sig = base64_decode('/VXgHvZAOdoz/OTa5+XJXzSGhjs=');
|
|
$check = hmac($test_ss, $test_token);
|
|
$res['hmac'] = ($check == $test_sig)
|
|
? 'pass' : sprintf("fail - '%s'", base64_encode($check));
|
|
|
|
if ($profile['use_bigmath']) {
|
|
// bigmath powmod
|
|
$test_server_public = '102773334773637418574009974502372885384288396853657336911033649141556441102566075470916498748591002884433213640712303846640842555822818660704173387461364443541327856226098159843042567251113889701110175072389560896826887426539315893475252988846151505416694218615764823146765717947374855806613410142231092856731';
|
|
$check = bmpowmod($g, $test_server_private, $p);
|
|
$res['bmpowmod-1'] = ($check == $test_server_public)
|
|
? 'pass' : sprintf("fail - '%s'", $check);
|
|
|
|
// long
|
|
$test_client_long = '133926731803116519408547886573524294471756220428015419404483437186057383311250738749035616354107518232016420809434801736658109316293127101479053449990587221774635063166689561125137927607200322073086097478667514042144489248048756916881344442393090205172004842481037581607299263456852036730858519133859409417564';
|
|
$res['long'] = (long($test_client_public) == $test_client_long)
|
|
? 'pass' : 'fail';
|
|
|
|
// bigmath powmod 2
|
|
$test_client_share = '19333275433742428703546496981182797556056709274486796259858099992516081822015362253491867310832140733686713353304595602619444380387600756677924791671971324290032515367930532292542300647858206600215875069588627551090223949962823532134061941805446571307168890255137575975911397744471376862555181588554632928402';
|
|
$check = bmpowmod($test_client_long, $test_server_private, $p);
|
|
$res['bmpowmod-2'] = ($check == $test_client_share)
|
|
? 'pass' : sprintf("fail - '%s'", $check);
|
|
|
|
// bin
|
|
$test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI=');
|
|
$check = bin($test_client_share);
|
|
$res['bin'] = ($check == $test_client_mac_s1)
|
|
? 'pass' : sprintf("fail - '%s'", base64_encode($check));
|
|
|
|
} else {
|
|
$res['bigmath'] = 'fail - big math functions are not available.';
|
|
}
|
|
|
|
// sha1_20
|
|
$test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI=');
|
|
$test_client_mac_s2 = base64_decode('0Mb2t9d/HvAZyuhbARJPYdx3+v4=');
|
|
$check = sha1_20($test_client_mac_s1);
|
|
$res['sha1_20'] = ($check == $test_client_mac_s2)
|
|
? 'pass' : sprintf("fail - '%s'", base64_encode($check));
|
|
|
|
// x_or
|
|
$test_client_mac_s3 = base64_decode('i36ZLYAJ1rYEx1VEHObrS8hgAg0=');
|
|
$check = x_or($test_client_mac_s2, $test_ss);
|
|
$res['x_or'] = ($check == $test_client_mac_s3)
|
|
? 'pass' : sprintf("fail - '%s'", base64_encode($check));
|
|
|
|
$out = "<table border=1 cellpadding=4>\n";
|
|
foreach ($res as $test => $stat) {
|
|
$code = substr($stat, 0, 4);
|
|
$color = ($code == 'pass') ? '#9f9'
|
|
: (($code == 'warn') ? '#ff9' : '#f99');
|
|
$out .= sprintf("<tr><th>%s</th><td style='background:%s'>%s</td></tr>\n", $test, $color, $stat);
|
|
}
|
|
$out .= "</table>";
|
|
|
|
wrap_html( $out );
|
|
}
|
|
|
|
|
|
// Support functions
|
|
|
|
/**
|
|
* Prefix the keys of an array with 'openid.'
|
|
* @param array $array
|
|
* @return array
|
|
*/
|
|
function append_openid ($array) {
|
|
$keys = array_keys($array);
|
|
$vals = array_values($array);
|
|
|
|
$r = array();
|
|
for ($i=0; $i<sizeof($keys); $i++)
|
|
$r['openid.' . $keys[$i]] = $vals[$i];
|
|
return $r;
|
|
}
|
|
|
|
/**
|
|
* Create a big math addition function
|
|
* @param string $l
|
|
* @param string $r
|
|
* @return string
|
|
* @url http://www.icosaedro.it/bigint Inspired by
|
|
*/
|
|
function bmadd($l, $r) {
|
|
if (function_exists('bcadd'))
|
|
return bcadd($l, $r);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_add($l, $r));
|
|
|
|
$l = strval($l); $r = strval($r);
|
|
$ll = strlen($l); $rl = strlen($r);
|
|
if ($ll < $rl) {
|
|
$l = str_repeat("0", $rl-$ll) . $l;
|
|
$o = $rl;
|
|
|
|
} elseif ( $ll > $rl ) {
|
|
$r = str_repeat("0", $ll-$rl) . $r;
|
|
$o = $ll;
|
|
|
|
} else {
|
|
$o = $ll;
|
|
}
|
|
|
|
$v = '';
|
|
$carry = 0;
|
|
|
|
for ($i = $o-1; $i >= 0; $i--) {
|
|
$d = (int)$l[$i] + (int)$r[$i] + $carry;
|
|
if ($d <= 9) {
|
|
$carry = 0;
|
|
|
|
} else {
|
|
$carry = 1;
|
|
$d -= 10;
|
|
}
|
|
$v = (string) $d . $v;
|
|
}
|
|
|
|
if ($carry > 0)
|
|
$v = "1" . $v;
|
|
|
|
return $v;
|
|
}
|
|
|
|
/**
|
|
* Create a big math comparison function
|
|
* @param string $l
|
|
* @param string $r
|
|
* @return string
|
|
*/
|
|
function bmcomp($l, $r) {
|
|
if (function_exists('bccomp'))
|
|
return bccomp($l, $r);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_cmp($l, $r));
|
|
|
|
$l = strval($l); $r = strval($r);
|
|
$ll = strlen($l); $lr = strlen($r);
|
|
if ($ll != $lr)
|
|
return ($ll > $lr) ? 1 : -1;
|
|
|
|
return strcmp($l, $r);
|
|
}
|
|
|
|
/**
|
|
* Create a big math division function
|
|
* @param string $l
|
|
* @param string $r
|
|
* @param int $z
|
|
* @return string
|
|
* @url http://www.icosaedro.it/bigint Inspired by
|
|
*/
|
|
function bmdiv($l, $r, $z = 0) {
|
|
if (function_exists('bcdiv'))
|
|
return ($z == 0) ? bcdiv($l, $r) : bcmod($l, $r);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(($z == 0) ? gmp_div_q($l, $r) : gmp_mod($l, $r));
|
|
|
|
$l = strval($l); $r = strval($r);
|
|
$v = '0';
|
|
|
|
while (true) {
|
|
if( bmcomp($l, $r) < 0 )
|
|
break;
|
|
|
|
$delta = strlen($l) - strlen($r);
|
|
if ($delta >= 1) {
|
|
$zeroes = str_repeat("0", $delta);
|
|
$r2 = $r . $zeroes;
|
|
|
|
if (strcmp($l, $r2) >= 0) {
|
|
$v = bmadd($v, "1" . $zeroes);
|
|
$l = bmsub($l, $r2);
|
|
|
|
} else {
|
|
$zeroes = str_repeat("0", $delta - 1);
|
|
$v = bmadd($v, "1" . $zeroes);
|
|
$l = bmsub($l, $r . $zeroes);
|
|
}
|
|
|
|
} else {
|
|
$l = bmsub($l, $r);
|
|
$v = bmadd($v, "1");
|
|
}
|
|
}
|
|
|
|
return ($z == 0) ? $v : $l;
|
|
}
|
|
|
|
/**
|
|
* Create a big math multiplication function
|
|
* @param string $l
|
|
* @param string $r
|
|
* @return string
|
|
* @url http://www.icosaedro.it/bigint Inspired by
|
|
*/
|
|
function bmmul($l, $r) {
|
|
if (function_exists('bcmul'))
|
|
return bcmul($l, $r);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_mul($l, $r));
|
|
|
|
$l = strval($l); $r = strval($r);
|
|
|
|
$v = '0';
|
|
$z = '';
|
|
|
|
for( $i = strlen($r)-1; $i >= 0; $i-- ){
|
|
$bd = (int) $r[$i];
|
|
$carry = 0;
|
|
$p = "";
|
|
for( $j = strlen($l)-1; $j >= 0; $j-- ){
|
|
$ad = (int) $l[$j];
|
|
$pd = $ad * $bd + $carry;
|
|
if( $pd <= 9 ){
|
|
$carry = 0;
|
|
} else {
|
|
$carry = (int) ($pd / 10);
|
|
$pd = $pd % 10;
|
|
}
|
|
$p = (string) $pd . $p;
|
|
}
|
|
if( $carry > 0 )
|
|
$p = (string) $carry . $p;
|
|
$p = $p . $z;
|
|
$z .= "0";
|
|
$v = bmadd($v, $p);
|
|
}
|
|
|
|
return $v;
|
|
}
|
|
|
|
/**
|
|
* Create a big math modulus function
|
|
* @param string $value
|
|
* @param string $mod
|
|
* @return string
|
|
*/
|
|
function bmmod( $value, $mod ) {
|
|
if (function_exists('bcmod'))
|
|
return bcmod($value, $mod);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_mod($value, $mod));
|
|
|
|
$r = bmdiv($value, $mod, 1);
|
|
return $r;
|
|
}
|
|
|
|
/**
|
|
* Create a big math power function
|
|
* @param string $value
|
|
* @param string $exponent
|
|
* @return string
|
|
*/
|
|
function bmpow ($value, $exponent) {
|
|
if (function_exists('bcpow'))
|
|
return bcpow($value, $exponent);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_pow($value, $exponent));
|
|
|
|
$r = '1';
|
|
while ($exponent) {
|
|
$r = bmmul($r, $value, 100);
|
|
$exponent--;
|
|
}
|
|
return (string)rtrim($r, '0.');
|
|
}
|
|
|
|
/**
|
|
* Create a big math 'powmod' function
|
|
* @param string $value
|
|
* @param string $exponent
|
|
* @param string $mod
|
|
* @return string
|
|
* @url http://php.net/manual/en/function.bcpowmod.php#72704 Borrowed from
|
|
*/
|
|
function bmpowmod ($value, $exponent, $mod) {
|
|
if (function_exists('bcpowmod'))
|
|
return bcpowmod($value, $exponent, $mod);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_powm($value, $exponent, $mod));
|
|
|
|
$r = '';
|
|
while ($exponent != '0') {
|
|
$t = bmmod($exponent, '4096');
|
|
$r = substr("000000000000" . decbin(intval($t)), -12) . $r;
|
|
$exponent = bmdiv($exponent, '4096');
|
|
}
|
|
|
|
$r = preg_replace("!^0+!","",$r);
|
|
|
|
if ($r == '')
|
|
$r = '0';
|
|
$value = bmmod($value, $mod);
|
|
$erb = strrev($r);
|
|
$q = '1';
|
|
$a[0] = $value;
|
|
|
|
for ($i = 1; $i < strlen($erb); $i++) {
|
|
$a[$i] = bmmod( bmmul($a[$i-1], $a[$i-1]), $mod );
|
|
}
|
|
|
|
for ($i = 0; $i < strlen($erb); $i++) {
|
|
if ($erb[$i] == "1") {
|
|
$q = bmmod( bmmul($q, $a[$i]), $mod );
|
|
}
|
|
}
|
|
|
|
return($q);
|
|
}
|
|
|
|
/**
|
|
* Create a big math subtraction function
|
|
* @param string $l
|
|
* @param string $r
|
|
* @return string
|
|
* @url http://www.icosaedro.it/bigint Inspired by
|
|
*/
|
|
function bmsub($l, $r) {
|
|
if (function_exists('bcsub'))
|
|
return bcsub($l, $r);
|
|
|
|
global $profile;
|
|
if ($profile['use_gmp'])
|
|
return gmp_strval(gmp_sub($l, $r));
|
|
|
|
|
|
$l = strval($l); $r = strval($r);
|
|
$ll = strlen($l); $rl = strlen($r);
|
|
|
|
if ($ll < $rl) {
|
|
$l = str_repeat("0", $rl-$ll) . $l;
|
|
$o = $rl;
|
|
} elseif ( $ll > $rl ) {
|
|
$r = str_repeat("0", $ll-$rl) . (string)$r;
|
|
$o = $ll;
|
|
} else {
|
|
$o = $ll;
|
|
}
|
|
|
|
if (strcmp($l, $r) >= 0) {
|
|
$sign = '';
|
|
} else {
|
|
$x = $l; $l = $r; $r = $x;
|
|
$sign = '-';
|
|
}
|
|
|
|
$v = '';
|
|
$carry = 0;
|
|
|
|
for ($i = $o-1; $i >= 0; $i--) {
|
|
$d = ($l[$i] - $r[$i]) - $carry;
|
|
if ($d < 0) {
|
|
$carry = 1;
|
|
$d += 10;
|
|
} else {
|
|
$carry = 0;
|
|
}
|
|
$v = (string) $d . $v;
|
|
}
|
|
|
|
return $sign . ltrim($v, '0');
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a binary value
|
|
* @param integer $n
|
|
* @return string
|
|
* @url http://openidenabled.com Borrowed from PHP-OpenID
|
|
*/
|
|
function bin ($n) {
|
|
$bytes = array();
|
|
while (bmcomp($n, 0) > 0) {
|
|
array_unshift($bytes, bmmod($n, 256));
|
|
$n = bmdiv($n, bmpow(2,8));
|
|
}
|
|
|
|
if ($bytes && ($bytes[0] > 127))
|
|
array_unshift($bytes, 0);
|
|
|
|
$b = '';
|
|
foreach ($bytes as $byte)
|
|
$b .= pack('C', $byte);
|
|
|
|
return $b;
|
|
}
|
|
|
|
|
|
/**
|
|
* Debug logging
|
|
* @param mixed $x
|
|
* @param string $m
|
|
*/
|
|
function debug ($x, $m = null) {
|
|
global $profile;
|
|
|
|
if (! isset($profile['debug']) || $profile['debug'] === false)
|
|
return true;
|
|
|
|
if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile']))
|
|
error_500('Cannot write to debug log: ' . $profile['logfile']);
|
|
|
|
if (is_array($x)) {
|
|
ob_start();
|
|
print_r($x);
|
|
$x = $m . ($m != null ? "\n" : '') . ob_get_clean();
|
|
|
|
} else {
|
|
$x .= "\n";
|
|
}
|
|
|
|
error_log($x . "\n", 3, $profile['logfile']);
|
|
}
|
|
|
|
|
|
/**
|
|
* Destroy a consumer's assoc handle
|
|
* @param string $id
|
|
*/
|
|
function destroy_assoc_handle ( $id ) {
|
|
debug("Destroying session: $id");
|
|
|
|
$sid = session_id();
|
|
session_write_close();
|
|
|
|
session_id($id);
|
|
session_start();
|
|
session_destroy();
|
|
|
|
session_id($sid);
|
|
session_start();
|
|
}
|
|
|
|
|
|
/**
|
|
* Return an error message to the user
|
|
* @param string $message
|
|
*/
|
|
function error_400 ( $message = 'Bad Request' ) {
|
|
header("HTTP/1.1 400 Bad Request");
|
|
wrap_html($message);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return an error message to the user
|
|
* @param string $message
|
|
*/
|
|
function error_403 ( $message = 'Forbidden' ) {
|
|
header("HTTP/1.1 403 Forbidden");
|
|
wrap_html($message);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return an error message to the user
|
|
* @param string $message
|
|
*/
|
|
function error_500 ( $message = 'Internal Server Error' ) {
|
|
header("HTTP/1.1 500 Internal Server Error");
|
|
wrap_html($message);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return an error message to the consumer
|
|
* @param string $message
|
|
*/
|
|
function error_get ( $url, $message = 'Bad Request') {
|
|
wrap_keyed_redirect($url, array('mode' => 'error', 'error' => $message));
|
|
}
|
|
|
|
|
|
/**
|
|
* Return an error message to the consumer
|
|
* @param string $message
|
|
*/
|
|
function error_post ( $message = 'Bad Request' ) {
|
|
header("HTTP/1.1 400 Bad Request");
|
|
echo ('error:' . $message);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Do an HMAC
|
|
* @param string $key
|
|
* @param string $data
|
|
* @param string $hash
|
|
* @return string
|
|
* @url http://php.net/manual/en/function.sha1.php#39492 Borrowed from
|
|
*/
|
|
function hmac($key, $data, $hash = 'sha1_20') {
|
|
$blocksize=64;
|
|
|
|
if (strlen($key) > $blocksize)
|
|
$key = $hash($key);
|
|
|
|
$key = str_pad($key, $blocksize,chr(0x00));
|
|
$ipad = str_repeat(chr(0x36),$blocksize);
|
|
$opad = str_repeat(chr(0x5c),$blocksize);
|
|
|
|
$h1 = $hash(($key ^ $ipad) . $data);
|
|
$hmac = $hash(($key ^ $opad) . $h1);
|
|
return $hmac;
|
|
}
|
|
|
|
|
|
if (! function_exists('http_build_query')) {
|
|
/**
|
|
* Create function if missing
|
|
* @param array $array
|
|
* @return string
|
|
*/
|
|
function http_build_query ($array) {
|
|
$r = array();
|
|
foreach ($array as $key => $val)
|
|
$r[] = sprintf('%s=%s', urlencode($key), urlencode($val));
|
|
return implode('&', $r);
|
|
}}
|
|
|
|
|
|
/**
|
|
* Turn a binary back into a long
|
|
* @param string $b
|
|
* @return integer
|
|
* @url http://openidenabled.com Borrowed from PHP-OpenID
|
|
*/
|
|
function long($b) {
|
|
$bytes = array_merge(unpack('C*', $b));
|
|
$n = 0;
|
|
foreach ($bytes as $byte) {
|
|
$n = bmmul($n, bmpow(2,8));
|
|
$n = bmadd($n, $byte);
|
|
}
|
|
return $n;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new consumer association
|
|
* @param integer $expiration
|
|
* @return array
|
|
*/
|
|
function new_assoc ( $expiration ) {
|
|
if (isset($_SESSION) && is_array($_SESSION)) {
|
|
$sid = session_id();
|
|
$dat = session_encode();
|
|
session_write_close();
|
|
}
|
|
|
|
session_start();
|
|
session_regenerate_id('false');
|
|
|
|
$id = session_id();
|
|
$shared_secret = new_secret();
|
|
debug('Started new assoc session: ' . $id);
|
|
|
|
$_SESSION = array();
|
|
$_SESSION['expiration'] = $expiration;
|
|
$_SESSION['shared_secret'] = base64_encode($shared_secret);
|
|
|
|
session_write_close();
|
|
|
|
if (isset($sid)) {
|
|
session_id($sid);
|
|
session_start();
|
|
$_SESSION = array();
|
|
session_decode($dat);
|
|
}
|
|
|
|
return array($id, $shared_secret);
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new shared secret
|
|
* @return string
|
|
*/
|
|
function new_secret () {
|
|
$r = '';
|
|
for($i=0; $i<20; $i++)
|
|
$r .= chr(mt_rand(0, 255));
|
|
|
|
debug("Generated new key: hash = '" . md5($r) . "', length = '" . strlen($r) . "'");
|
|
return $r;
|
|
}
|
|
|
|
|
|
/**
|
|
* Random number generation
|
|
* @param integer max
|
|
* @return integer
|
|
*/
|
|
function random ( $max ) {
|
|
if (strlen($max) < 4)
|
|
return mt_rand(1, $max - 1);
|
|
|
|
$r = '';
|
|
for($i=1; $i<strlen($max) - 1; $i++)
|
|
$r .= mt_rand(0,9);
|
|
$r .= mt_rand(1,9);
|
|
|
|
return $r;
|
|
}
|
|
|
|
/**
|
|
* Get the shared secret and expiration time for the specified assoc_handle
|
|
* @param string $handle assoc_handle to look up
|
|
* @return array (shared_secret, expiration_time)
|
|
*/
|
|
function secret ( $handle ) {
|
|
if (! preg_match('/^\w+$/', $handle))
|
|
return array(false, 0);
|
|
|
|
if (isset($_SESSION) && is_array($_SESSION)) {
|
|
$sid = session_id();
|
|
$dat = session_encode();
|
|
session_write_close();
|
|
}
|
|
|
|
session_id($handle);
|
|
session_start();
|
|
debug('Started session to acquire key: ' . session_id());
|
|
|
|
$secret = session_is_registered('shared_secret')
|
|
? base64_decode($_SESSION['shared_secret'])
|
|
: false;
|
|
|
|
$expiration = session_is_registered('expiration')
|
|
? $_SESSION['expiration']
|
|
: null;
|
|
|
|
session_write_close();
|
|
|
|
if (isset($sid)) {
|
|
session_id($sid);
|
|
session_start();
|
|
$_SESSION = array();
|
|
session_decode($dat);
|
|
}
|
|
|
|
debug("Found key: hash = '" . md5($secret) . "', length = '" . strlen($secret) . "', expiration = '$expiration'");
|
|
return array($secret, $expiration);
|
|
}
|
|
|
|
|
|
/**
|
|
* Do an internal self check
|
|
* @global array $profile
|
|
* @global array $sreg
|
|
*/
|
|
function self_check () {
|
|
global $profile, $sreg;
|
|
|
|
// if (! isset($profile) || ! is_array($profile))
|
|
// error_500('No configuration found, you shouldn\'t access this file directly.');
|
|
|
|
if (version_compare(phpversion(), '4.2.0', 'lt'))
|
|
error_500('The required minimum version of PHP is 4.2.0, you are running ' . phpversion());
|
|
|
|
$extension_r = array('session', 'pcre');
|
|
foreach ($extension_r as $x) {
|
|
if (! extension_loaded($x))
|
|
@dl($x);
|
|
if (! extension_loaded($x))
|
|
error_500("Required extension '$x' is missing.");
|
|
}
|
|
|
|
// $extension_b = array('suhosin');
|
|
// foreach ($extension_b as $x) {
|
|
// if (extension_loaded($x) &! $profile["allow_$x"])
|
|
// error_500("phpMyID is not compatible with '$x'");
|
|
// }
|
|
//
|
|
// $keys = array('auth_username', 'auth_password');
|
|
// foreach ($keys as $key) {
|
|
// if (! array_key_exists($key, $profile))
|
|
// error_500("'$key' is missing from your profile.");
|
|
// }
|
|
|
|
if (! isset($sreg) || ! is_array($sreg))
|
|
$sreg = array();
|
|
}
|
|
|
|
|
|
/**
|
|
* Do SHA1 20 byte encryption
|
|
* @param string $v
|
|
* @return string
|
|
* @url http://openidenabled.com Borrowed from PHP-OpenID
|
|
*/
|
|
function sha1_20 ($v) {
|
|
if (version_compare(phpversion(), '5.0.0', 'ge'))
|
|
return sha1($v, true);
|
|
|
|
$hex = sha1($v);
|
|
$r = '';
|
|
for ($i = 0; $i < 40; $i += 2) {
|
|
$hexcode = substr($hex, $i, 2);
|
|
$charcode = base_convert($hexcode, 16, 10);
|
|
$r .= chr($charcode);
|
|
}
|
|
return $r;
|
|
}
|
|
|
|
|
|
/**
|
|
* Look for the point of differentiation in two strings
|
|
* @param string $a
|
|
* @param string $b
|
|
* @return int
|
|
*/
|
|
function str_diff_at ($a, $b) {
|
|
if ($a == $b)
|
|
return -1;
|
|
$n = min(strlen($a), strlen($b));
|
|
for ($i = 0; $i < $n; $i++)
|
|
if ($a[$i] != $b[$i])
|
|
return $i;
|
|
return $n;
|
|
}
|
|
|
|
|
|
if (! function_exists('sys_get_temp_dir') && ini_get('open_basedir') == false) {
|
|
/**
|
|
* Create function if missing
|
|
* @return string
|
|
*/
|
|
function sys_get_temp_dir () {
|
|
$keys = array('TMP', 'TMPDIR', 'TEMP');
|
|
foreach ($keys as $key) {
|
|
if (isset($_ENV[$key]) && is_dir($_ENV[$key]) && is_writable($_ENV[$key]))
|
|
return realpath($_ENV[$key]);
|
|
}
|
|
|
|
$tmp = tempnam(false, null);
|
|
if (file_exists($tmp)) {
|
|
$dir = realpath(dirname($tmp));
|
|
unlink($tmp);
|
|
return realpath($dir);
|
|
}
|
|
|
|
return realpath(dirname(__FILE__));
|
|
}} elseif (! function_exists('sys_get_temp_dir')) {
|
|
function sys_get_temp_dir () {
|
|
return realpath(dirname(__FILE__));
|
|
}}
|
|
|
|
|
|
/**
|
|
* Determine if a child URL actually decends from the parent, and that the
|
|
* parent is a good URL.
|
|
* THIS IS EXPERIMENTAL
|
|
* @param string $parent
|
|
* @param string $child
|
|
* @return bool
|
|
*/
|
|
function url_descends ( $child, $parent ) {
|
|
if ($child == $parent)
|
|
return true;
|
|
|
|
$keys = array();
|
|
$parts = array();
|
|
|
|
$req = array('scheme', 'host');
|
|
$bad = array('fragment', 'pass', 'user');
|
|
|
|
foreach (array('parent', 'child') as $name) {
|
|
$parts[$name] = @parse_url($$name);
|
|
if ($parts[$name] === false)
|
|
return false;
|
|
|
|
$keys[$name] = array_keys($parts[$name]);
|
|
|
|
if (array_intersect($keys[$name], $req) != $req)
|
|
return false;
|
|
|
|
if (array_intersect($keys[$name], $bad) != array())
|
|
return false;
|
|
|
|
if (! preg_match('/^https?$/i', strtolower($parts[$name]['scheme'])))
|
|
return false;
|
|
|
|
if (! array_key_exists('port', $parts[$name]))
|
|
$parts[$name]['port'] = (strtolower($parts[$name]['scheme']) == 'https') ? 443 : 80;
|
|
|
|
if (! array_key_exists('path', $parts[$name]))
|
|
$parts[$name]['path'] = '/';
|
|
}
|
|
|
|
// port and scheme must match
|
|
if ($parts['parent']['scheme'] != $parts['child']['scheme'] ||
|
|
$parts['parent']['port'] != $parts['child']['port'])
|
|
return false;
|
|
|
|
// compare the hosts by reversing the strings
|
|
$cr_host = strtolower(strrev($parts['child']['host']));
|
|
$pr_host = strtolower(strrev($parts['parent']['host']));
|
|
|
|
$break = str_diff_at($cr_host, $pr_host);
|
|
if ($break >= 0 && ($pr_host[$break] != '*' || substr_count(substr($pr_host, 0, $break), '.') < 2))
|
|
return false;
|
|
|
|
// now compare the paths
|
|
$break = str_diff_at($parts['child']['path'], $parts['parent']['path']);
|
|
if ($break >= 0
|
|
&& ($break < strlen($parts['parent']['path']) && $parts['parent']['path'][$break] != '*')
|
|
|| ($break > strlen($parts['child']['path'])))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a user session
|
|
* @global array $profile
|
|
* @global array $proto
|
|
*/
|
|
function user_session () {
|
|
global $proto, $profile;
|
|
|
|
session_name('phpMyID_Server');
|
|
@session_start();
|
|
|
|
$profile['authorized'] = (isset($_SESSION['auth_username'])
|
|
&& $_SESSION['auth_username'] == $profile['auth_username'])
|
|
? true
|
|
: false;
|
|
|
|
debug('Started user session: ' . session_id() . ' Auth? ' . $profile['authorized']);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return HTML
|
|
* @global string $charset
|
|
* @param string $message
|
|
*/
|
|
function wrap_html ( $message ) {
|
|
global $charset, $profile;
|
|
header('Content-Type: text/html; charset=' . $charset);
|
|
$html= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html>
|
|
<head>
|
|
<title>phpMyID</title>
|
|
<link rel="openid.server" href="' . $profile['req_url'] . '" />
|
|
<link rel="openid.delegate" href="' . $profile['idp_url'] . '" />
|
|
' . implode("\n", $profile['opt_headers']) . '
|
|
<meta name="charset" content="' . $charset . '" />
|
|
<meta name="robots" content="noindex,nofollow" />
|
|
</head>
|
|
<body>
|
|
<p>' . $message . '</p>
|
|
</body>
|
|
</html>
|
|
';
|
|
error_log($html);
|
|
echo $html;
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a key-value pair in plain text
|
|
* @global string $charset
|
|
* @param array $keys
|
|
*/
|
|
function wrap_kv ( $keys ) {
|
|
global $charset;
|
|
|
|
debug($keys, 'Wrapped key/vals');
|
|
header('Content-Type: text/plain; charset=' . $charset);
|
|
foreach ($keys as $key => $value)
|
|
printf("%s:%s\n", $key, $value);
|
|
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Redirect, with OpenID keys
|
|
* @param string $url
|
|
* @param array @keys
|
|
*/
|
|
function wrap_keyed_redirect ($url, $keys) {
|
|
$keys = append_openid($keys);
|
|
debug($keys, 'Location keys');
|
|
|
|
$q = strpos($url, '?') ? '&' : '?';
|
|
wrap_redirect($url . $q . http_build_query($keys));
|
|
}
|
|
|
|
|
|
/**
|
|
* Redirect the browser
|
|
* @global string $charset
|
|
* @param string $url
|
|
*/
|
|
function wrap_redirect ($url) {
|
|
header('HTTP/1.1 302 Found');
|
|
header('Location: ' . $url);
|
|
debug('Location: ' . $url);
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
* Return an HTML refresh
|
|
* @global string $charset
|
|
* @param string $url
|
|
*/
|
|
function wrap_refresh ($url) {
|
|
global $charset;
|
|
|
|
header('Content-Type: text/html; charset=' . $charset);
|
|
echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html>
|
|
<head>
|
|
<title>phpMyID</title>
|
|
<meta http-equiv="refresh" content="0;url=' . $url . '">
|
|
</head>
|
|
<body>
|
|
<p>Redirecting to <a href="' . $url . '">' . $url . '</a></p>
|
|
</body>
|
|
</html>
|
|
';
|
|
|
|
debug('Refresh: ' . $url);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Implement binary x_or
|
|
* @param string $a
|
|
* @param string $b
|
|
* @return string
|
|
*/
|
|
function x_or ($a, $b) {
|
|
$r = "";
|
|
|
|
for ($i = 0; $i < strlen($b); $i++)
|
|
$r .= $a[$i] ^ $b[$i];
|
|
debug("Xor size: " . strlen($r));
|
|
return $r;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* App Initialization
|
|
*/
|
|
// Determine the charset to use
|
|
$GLOBALS['charset'] = 'iso-8859-1';
|
|
|
|
// Set the internal encoding
|
|
if (function_exists('mb_internal_encoding'))
|
|
mb_internal_encoding($charset);
|
|
|
|
// Avoid problems with non-default arg_separator.output settings
|
|
// Credit for this goes to user 'prelog' on the forums
|
|
ini_set('arg_separator.output', '&');
|
|
|
|
// Do a check to be sure everything is set up correctly
|
|
self_check();
|
|
|
|
|
|
/**
|
|
* Determine the HTTP request port
|
|
* @name $port
|
|
* @global integer $GLOBALS['port']
|
|
*/
|
|
$GLOBALS['port'] = ((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' && $_SERVER['SERVER_PORT'] == 443)
|
|
|| $_SERVER['SERVER_PORT'] == 80)
|
|
? ''
|
|
: ':' . $_SERVER['SERVER_PORT'];
|
|
|
|
|
|
error_log($_SERVER['HTTP_HOST']);
|
|
/**
|
|
* Determine the HTTP request protocol
|
|
* @name $proto
|
|
* @global string $GLOBALS['proto']
|
|
*/
|
|
$GLOBALS['proto'] = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
|
|
|
|
// Set the authorization state - DO NOT OVERRIDE
|
|
$profile['authorized'] = false;
|
|
|
|
// Set a default IDP URL
|
|
if (! array_key_exists('idp_url', $profile))
|
|
$profile['idp_url'] = sprintf("%s://%s%s%s",
|
|
$proto,
|
|
$_SERVER['SERVER_NAME'],
|
|
$port,
|
|
$_SERVER['PHP_SELF']);
|
|
|
|
// Determine the requested URL - DO NOT OVERRIDE
|
|
$profile['req_url'] = sprintf("%s://%s%s",
|
|
$proto,
|
|
$_SERVER['HTTP_HOST'],
|
|
// $port,//host already includes the path
|
|
$_SERVER["REQUEST_URI"]);
|
|
|
|
error_log($profile['req_url']);
|
|
|
|
// Set the default allowance for testing
|
|
if (! array_key_exists('allow_test', $profile))
|
|
$profile['allow_test'] = false;
|
|
|
|
// Set the default allowance for gmp
|
|
if (! array_key_exists('allow_gmp', $profile))
|
|
$profile['allow_gmp'] = false;
|
|
|
|
// Set the default force bigmath - BAD IDEA to override this
|
|
if (! array_key_exists('force_bigmath', $profile))
|
|
$profile['force_bigmath'] = false;
|
|
|
|
// Determine if GMP is usable
|
|
$profile['use_gmp'] = (extension_loaded('gmp') && $profile['allow_gmp']) ? true : false;
|
|
|
|
// Determine if I can perform big math functions
|
|
$profile['use_bigmath'] = (extension_loaded('bcmath') || $profile['use_gmp'] || $profile['force_bigmath']) ? true : false;
|
|
|
|
// Set a default authentication domain
|
|
if (! array_key_exists('auth_domain', $profile))
|
|
$profile['auth_domain'] = $profile['req_url'] . ' ' . $profile['idp_url'];
|
|
|
|
// Set a default authentication realm
|
|
if (! array_key_exists('auth_realm', $profile))
|
|
$profile['auth_realm'] = 'phpMyID';
|
|
|
|
// Determine the realm for digest authentication - DO NOT OVERRIDE
|
|
$profile['php_realm'] = $profile['auth_realm'] . (ini_get('safe_mode') ? '-' . getmyuid() : '');
|
|
|
|
// Set a default lifetime - the lesser of GC and cache time
|
|
if (! array_key_exists('lifetime', $profile)) {
|
|
$sce = session_cache_expire() * 60;
|
|
$gcm = ini_get('session.gc_maxlifetime');
|
|
$profile['lifetime'] = $sce < $gcm ? $sce : $gcm;
|
|
}
|
|
|
|
// Set a default log file
|
|
if (! array_key_exists('logfile', $profile))
|
|
$profile['logfile'] = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $profile['auth_realm'] . '.debug.log';
|
|
|
|
|
|
/*
|
|
* Optional Initialization
|
|
*/
|
|
// Setup optional headers
|
|
$profile['opt_headers'] = array();
|
|
|
|
// Determine if I should add microid stuff
|
|
if (array_key_exists('microid', $profile)) {
|
|
$hash = sha1($profile['idp_url']);
|
|
$values = is_array($profile['microid']) ? $profile['microid'] : array($profile['microid']);
|
|
|
|
foreach ($values as $microid) {
|
|
preg_match('/^([a-z]+)/i', $microid, $mtx);
|
|
$profile['opt_headers'][] = sprintf('<meta name="microid" content="%s+%s:sha1:%s" />', $mtx[1], $proto, sha1(sha1($microid) . $hash));
|
|
}
|
|
}
|
|
|
|
// Determine if I should add pavatar stuff
|
|
if (array_key_exists('pavatar', $profile))
|
|
$profile['opt_headers'][] = sprintf('<link rel="pavatar" href="%s" />', $profile['pavatar']);
|
|
|
|
|
|
/*
|
|
* Do it
|
|
*/
|
|
// Decide which runmode, based on user request or default
|
|
$run_mode = (isset($_REQUEST['openid_mode'])
|
|
&& in_array($_REQUEST['openid_mode'], $known['openid_modes']))
|
|
? $_REQUEST['openid_mode']
|
|
: 'no';
|
|
|
|
// Run in the determined runmode
|
|
debug("Run mode: $run_mode at: " . time());
|
|
debug($_REQUEST, 'Request params');
|
|
call_user_func($run_mode . '_mode');
|
|
?>
|