diff options
author | Robin Appelman <icewind@owncloud.com> | 2012-07-24 00:02:01 +0200 |
---|---|---|
committer | Robin Appelman <icewind@owncloud.com> | 2012-07-24 00:02:01 +0200 |
commit | 796768577154dc9cc2542e33d8d81c6a5fa77dea (patch) | |
tree | f9dd3a9aeb1baa8f9c65f5b8e67770c5257e4b7c /3rdparty | |
parent | 59364366d8c69ca217fb817535d1b5d1df316841 (diff) | |
download | nextcloud-server-796768577154dc9cc2542e33d8d81c6a5fa77dea.tar.gz nextcloud-server-796768577154dc9cc2542e33d8d81c6a5fa77dea.zip |
move some openid files to 3rdparty
Diffstat (limited to '3rdparty')
-rw-r--r-- | 3rdparty/openid/class.openid.v3.php | 326 | ||||
-rw-r--r-- | 3rdparty/openid/phpmyid.php | 1707 |
2 files changed, 2033 insertions, 0 deletions
diff --git a/3rdparty/openid/class.openid.v3.php b/3rdparty/openid/class.openid.v3.php new file mode 100644 index 00000000000..eeb31986659 --- /dev/null +++ b/3rdparty/openid/class.openid.v3.php @@ -0,0 +1,326 @@ +<?php +/* + FREE TO USE + Under License: GPLv3 + Simple OpenID PHP Class + + Some modifications by Eddie Roosenmaallen, eddie@roosenmaallen.com + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +This Class was written to make easy for you to integrate OpenID on your website. +This is just a client, which checks for user's identity. This Class Requires CURL Module. +It should be easy to use some other HTTP Request Method, but remember, often OpenID servers +are using SSL. +We need to be able to perform SSL Verification on the background to check for valid signature. + +HOW TO USE THIS CLASS: + STEP 1) + $openid = new SimpleOpenID; + :: SET IDENTITY :: + $openid->SetIdentity($_POST['openid_url']); + :: SET RETURN URL :: + $openid->SetApprovedURL('http://www.yoursite.com/return.php'); // Script which handles a response from OpenID Server + :: SET TRUST ROOT :: + $openid->SetTrustRoot('http://www.yoursite.com/'); + :: FETCH SERVER URL FROM IDENTITY PAGE :: [Note: It is recomended to cache this (Session, Cookie, Database)] + $openid->GetOpenIDServer(); // Returns false if server is not found + :: REDIRECT USER TO OPEN ID SERVER FOR APPROVAL :: + + :: (OPTIONAL) SET OPENID SERVER :: + $openid->SetOpenIDServer($server_url); // If you have cached previously this, you don't have to call GetOpenIDServer and set value this directly + + STEP 2) + Once user gets returned we must validate signature + :: VALIDATE REQUEST :: + true|false = $openid->ValidateWithServer(); + + ERRORS: + array = $openid->GetError(); // Get latest Error code + + FIELDS: + OpenID allowes you to retreive a profile. To set what fields you'd like to get use (accepts either string or array): + $openid->SetRequiredFields(array('email','fullname','dob','gender','postcode','country','language','timezone')); + or + $openid->SetOptionalFields('postcode'); + +IMPORTANT TIPS: +OPENID as is now, is not trust system. It is a great single-sign on method. If you want to +store information about OpenID in your database for later use, make sure you handle url identities +properly. + For example: + https://steve.myopenid.com/ + https://steve.myopenid.com + http://steve.myopenid.com/ + http://steve.myopenid.com + ... are representing one single user. Some OpenIDs can be in format openidserver.com/users/user/ - keep this in mind when storing identities + + To help you store an OpenID in your DB, you can use function: + $openid_db_safe = $openid->OpenID_Standarize($upenid); + This may not be comatible with current specs, but it works in current enviroment. Use this function to get openid + in one format like steve.myopenid.com (without trailing slashes and http/https). + Use output to insert Identity to database. Don't use this for validation - it may fail. + +*/ + +class SimpleOpenID{ + var $openid_url_identity; + var $URLs = array(); + var $error = array(); + var $fields = array( + 'required' => array(), + 'optional' => array(), + ); + + function SimpleOpenID(){ + if (!function_exists('curl_exec')) { + die('Error: Class SimpleOpenID requires curl extension to work'); + } + } + function SetOpenIDServer($a){ + $this->URLs['openid_server'] = $a; + } + function SetTrustRoot($a){ + $this->URLs['trust_root'] = $a; + } + function SetCancelURL($a){ + $this->URLs['cancel'] = $a; + } + function SetApprovedURL($a){ + $this->URLs['approved'] = $a; + } + function SetRequiredFields($a){ + if (is_array($a)){ + $this->fields['required'] = $a; + }else{ + $this->fields['required'][] = $a; + } + } + function SetOptionalFields($a){ + if (is_array($a)){ + $this->fields['optional'] = $a; + }else{ + $this->fields['optional'][] = $a; + } + } + function SetIdentity($a){ // Set Identity URL + if ((stripos($a, 'http://') === false) + && (stripos($a, 'https://') === false)){ + $a = 'http://'.$a; + } +/* + $u = parse_url(trim($a)); + if (!isset($u['path'])){ + $u['path'] = '/'; + }else if(substr($u['path'],-1,1) == '/'){ + $u['path'] = substr($u['path'], 0, strlen($u['path'])-1); + } + if (isset($u['query'])){ // If there is a query string, then use identity as is + $identity = $a; + }else{ + $identity = $u['scheme'] . '://' . $u['host'] . $u['path']; + } +//*/ + $this->openid_url_identity = $a; + } + function GetIdentity(){ // Get Identity + return $this->openid_url_identity; + } + function GetError(){ + $e = $this->error; + return array('code'=>$e[0],'description'=>$e[1]); + } + + function ErrorStore($code, $desc = null){ + $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.'; + if ($desc == null){ + $desc = $errs[$code]; + } + $this->error = array($code,$desc); + } + + function IsError(){ + if (count($this->error) > 0){ + return true; + }else{ + return false; + } + } + + function splitResponse($response) { + $r = array(); + $response = explode("\n", $response); + foreach($response as $line) { + $line = trim($line); + if ($line != "") { + list($key, $value) = explode(":", $line, 2); + $r[trim($key)] = trim($value); + } + } + return $r; + } + + function OpenID_Standarize($openid_identity = null){ + if ($openid_identity === null) + $openid_identity = $this->openid_url_identity; + + $u = parse_url(strtolower(trim($openid_identity))); + + if (!isset($u['path']) || ($u['path'] == '/')) { + $u['path'] = ''; + } + if(substr($u['path'],-1,1) == '/'){ + $u['path'] = substr($u['path'], 0, strlen($u['path'])-1); + } + if (isset($u['query'])){ // If there is a query string, then use identity as is + return $u['host'] . $u['path'] . '?' . $u['query']; + }else{ + return $u['host'] . $u['path']; + } + } + + function array2url($arr){ // converts associated array to URL Query String + if (!is_array($arr)){ + return false; + } + $query = ''; + foreach($arr as $key => $value){ + $query .= $key . "=" . $value . "&"; + } + return $query; + } + function FSOCK_Request($url, $method="GET", $params = ""){ + $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds + if (!$fp) { + $this->ErrorStore('OPENID_SOCKETERROR', $errstr); + return false; + } else { + $request = $method . " /server HTTP/1.0\r\n"; + $request .= "User-Agent: Simple OpenID PHP Class (http://www.phpclasses.org/simple_openid)\r\n"; + $request .= "Connection: close\r\n\r\n"; + fwrite($fp, $request); + stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds + $res = fread($fp, 2000); + $info = stream_get_meta_data($fp); + fclose($fp); + + if ($info['timed_out']) { + $this->ErrorStore('OPENID_SOCKETTIMEOUT'); + } else { + return $res; + } + } + } + function CURL_Request($url, $method="GET", $params = "") { // Remember, SSL MUST BE SUPPORTED + if (is_array($params)) $params = $this->array2url($params); + $curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : "")); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET")); + curl_setopt($curl, CURLOPT_POST, ($method == "POST")); + if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + $response = curl_exec($curl); + + if (curl_errno($curl) == 0){ + $response; + }else{ + $this->ErrorStore('OPENID_CURL', curl_error($curl)); + } + return $response; + } + + function HTML2OpenIDServer($content) { + $get = array(); + + // Get details of their OpenID server and (optional) delegate + preg_match_all('/<link[^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1); + preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*\/?>/i', $content, $matches2); + $servers = array_merge($matches1[2], $matches2[1]); + + preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1); + + preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2); + + $delegates = array_merge($matches1[1], $matches2[1]); + + $ret = array($servers, $delegates); + return $ret; + } + + function GetOpenIDServer(){ + $response = $this->CURL_Request($this->openid_url_identity); + list($servers, $delegates) = $this->HTML2OpenIDServer($response); + if (count($servers) == 0){ + $this->ErrorStore('OPENID_NOSERVERSFOUND'); + return false; + } + if (isset($delegates[0]) + && ($delegates[0] != "")){ + $this->SetIdentity($delegates[0]); + } + $this->SetOpenIDServer($servers[0]); + return $servers[0]; + } + + function GetRedirectURL(){ + $params = array(); + $params['openid.return_to'] = urlencode($this->URLs['approved']); + $params['openid.mode'] = 'checkid_setup'; + $params['openid.identity'] = urlencode($this->openid_url_identity); + $params['openid.trust_root'] = urlencode($this->URLs['trust_root']); + + if (isset($this->fields['required']) + && (count($this->fields['required']) > 0)) { + $params['openid.sreg.required'] = implode(',',$this->fields['required']); + } + if (isset($this->fields['optional']) + && (count($this->fields['optional']) > 0)) { + $params['openid.sreg.optional'] = implode(',',$this->fields['optional']); + } + return $this->URLs['openid_server'] . "?". $this->array2url($params); + } + + function Redirect(){ + $redirect_to = $this->GetRedirectURL(); + if (headers_sent()){ // Use JavaScript to redirect if content has been previously sent (not recommended, but safe) + echo '<script language="JavaScript" type="text/javascript">window.location=\''; + echo $redirect_to; + echo '\';</script>'; + }else{ // Default Header Redirect + header('Location: ' . $redirect_to); + } + } + + function ValidateWithServer(){ + $params = array( + 'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']), + 'openid.signed' => urlencode($_GET['openid_signed']), + 'openid.sig' => urlencode($_GET['openid_sig']) + ); + // Send only required parameters to confirm validity + $arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed'])); + for ($i=0; $i<count($arr_signed); $i++){ + $s = str_replace('sreg_','sreg.', $arr_signed[$i]); + $c = $_GET['openid_' . $arr_signed[$i]]; + // if ($c != ""){ + $params['openid.' . $s] = urlencode($c); + // } + } + $params['openid.mode'] = "check_authentication"; + + $openid_server = $this->GetOpenIDServer(); + if ($openid_server == false){ + return false; + } + $response = $this->CURL_Request($openid_server,'POST',$params); + $data = $this->splitResponse($response); + + if ($data['is_valid'] == "true") { + return true; + }else{ + return false; + } + } +} diff --git a/3rdparty/openid/phpmyid.php b/3rdparty/openid/phpmyid.php new file mode 100644 index 00000000000..13fd31c47ca --- /dev/null +++ b/3rdparty/openid/phpmyid.php @@ -0,0 +1,1707 @@ +<?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; + global $IDENTITY; + + // 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.'); + + $profile['idp_url']=$IDENTITY; + if (isset($_SERVER['PHP_AUTH_USER']) && $profile['authorized'] === false && $_SERVER['PHP_AUTH_USER']==$USERNAME) { + if (OCP\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 = isset($_REQUEST['openid_return_to']) + ? $_REQUEST['openid_return_to'] + : error_400('Missing return_to'); + + $identity = isset($_REQUEST['openid_identity']) + ? $_REQUEST['openid_identity'] + : error_get($return_to, 'Missing identity'); + + $assoc_handle = isset($_REQUEST['openid_assoc_handle']) + ? $_REQUEST['openid_assoc_handle'] + : null; + + $trust_root = isset($_REQUEST['openid_trust_root']) + ? $_REQUEST['openid_trust_root'] + : $return_to; + + $sreg_required = isset($_REQUEST['openid_sreg_required']) + ? $_REQUEST['openid_sreg.required'] + : ''; + + $sreg_optional = isset($_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 && (! isset($_SESSION['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 ($USERNAME=='' || $_SESSION['openid_auth'] === false || $USERNAME != $_SESSION['openid_user']) { + // users can only be logged in to one url at a time + $_SESSION['openid_user'] = 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 $USERNAME, $profile; + $tmpl = new OCP\Template( 'user_openid', 'nomode', 'guest' ); + if(substr($profile['req_url'],-1,1)!=='/'){//the identity should always end with a / + $profile['req_url'].='/'; + } + $tmpl->addHeader('link',array('rel'=>'openid.server', 'href'=>$profile['req_url'])); + $tmpl->addHeader('link',array('rel'=>'openid.delegate', 'href'=>$profile['idp_url'])); + $tmpl->assign('user',$USERNAME); + $tmpl->printPage(); +} + + +/** + * 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'; + } + + // 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"; + } +} + + +/** + * 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); + if (OCP\Config::getSystemValue( "forcessl", false )) { + ini_set("session.cookie_secure", "on"); + } + 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(); + } + + if (OCP\Config::getSystemValue( "forcessl", false )) { + ini_set("session.cookie_secure", "on"); + } + 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); + if (OCP\Config::getSystemValue( "forcessl", false )) { + ini_set("session.cookie_secure", "on"); + } + session_start(); + debug('Started session to acquire key: ' . session_id()); + + $secret = isset($_SESSION['shared_secret']) + ? base64_decode($_SESSION['shared_secret']) + : false; + + $expiration = isset($_SESSION['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; +} + +/** + * 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'); + if (OCP\Config::getSystemValue( "forcessl", false )) { + ini_set("session.cookie_secure", "on"); + } + @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> +'; + 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']; + + +/** + * 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; + +global $IDENTITY; +global $USERNAME; + +// Set a default IDP URL +if (! array_key_exists('idp_url', $profile)) + $profile['idp_url'] = $IDENTITY; + +//Determine the requested URL - DO NOT OVERRIDE +$profile['req_url'] = sprintf("%s://%s%s", + $proto, + OCP\Util::getServerHost(), +// $port,//host already includes the path + $_SERVER["REQUEST_URI"]); + + +// 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'] = 'ownCloud'; + +// 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'] = 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'); |