Server.php

Go to the documentation of this file.
00001 <?php
00002 
00095 require_once "Auth/OpenID.php";
00096 require_once "Auth/OpenID/Association.php";
00097 require_once "Auth/OpenID/CryptUtil.php";
00098 require_once "Auth/OpenID/BigMath.php";
00099 require_once "Auth/OpenID/DiffieHellman.php";
00100 require_once "Auth/OpenID/KVForm.php";
00101 require_once "Auth/OpenID/TrustRoot.php";
00102 require_once "Auth/OpenID/ServerRequest.php";
00103 require_once "Auth/OpenID/Message.php";
00104 require_once "Auth/OpenID/Nonce.php";
00105 
00106 define('AUTH_OPENID_HTTP_OK', 200);
00107 define('AUTH_OPENID_HTTP_REDIRECT', 302);
00108 define('AUTH_OPENID_HTTP_ERROR', 400);
00109 
00113 global $_Auth_OpenID_Request_Modes;
00114 $_Auth_OpenID_Request_Modes = array('checkid_setup',
00115                                     'checkid_immediate');
00116 
00120 define('Auth_OpenID_ENCODE_KVFORM', 'kfvorm');
00121 
00125 define('Auth_OpenID_ENCODE_URL', 'URL/redirect');
00126 
00130 define('Auth_OpenID_ENCODE_HTML_FORM', 'HTML form');
00131 
00135 function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError')
00136 {
00137     return is_a($obj, $cls);
00138 }
00139 
00147 class Auth_OpenID_ServerError {
00151     function Auth_OpenID_ServerError($message = null, $text = null,
00152                                      $reference = null, $contact = null)
00153     {
00154         $this->message = $message;
00155         $this->text = $text;
00156         $this->contact = $contact;
00157         $this->reference = $reference;
00158     }
00159 
00160     function getReturnTo()
00161     {
00162         if ($this->message &&
00163             $this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) {
00164             return $this->message->getArg(Auth_OpenID_OPENID_NS,
00165                                           'return_to');
00166         } else {
00167             return null;
00168         }
00169     }
00170 
00175     function hasReturnTo()
00176     {
00177         return $this->getReturnTo() !== null;
00178     }
00179 
00185     function encodeToURL()
00186     {
00187         if (!$this->message) {
00188             return null;
00189         }
00190 
00191         $msg = $this->toMessage();
00192         return $msg->toURL($this->getReturnTo());
00193     }
00194 
00201     function encodeToKVForm()
00202     {
00203         return Auth_OpenID_KVForm::fromArray(
00204                                       array('mode' => 'error',
00205                                             'error' => $this->toString()));
00206     }
00207 
00208     function toFormMarkup($form_tag_attrs=null)
00209     {
00210         $msg = $this->toMessage();
00211         return $msg->toFormMarkup($this->getReturnTo(), $form_tag_attrs);
00212     }
00213 
00214     function toHTML($form_tag_attrs=null)
00215     {
00216         return Auth_OpenID::autoSubmitHTML(
00217                       $this->toFormMarkup($form_tag_attrs));
00218     }
00219 
00220     function toMessage()
00221     {
00222         // Generate a Message object for sending to the relying party,
00223         // after encoding.
00224         $namespace = $this->message->getOpenIDNamespace();
00225         $reply = new Auth_OpenID_Message($namespace);
00226         $reply->setArg(Auth_OpenID_OPENID_NS, 'mode', 'error');
00227         $reply->setArg(Auth_OpenID_OPENID_NS, 'error', $this->toString());
00228 
00229         if ($this->contact !== null) {
00230             $reply->setArg(Auth_OpenID_OPENID_NS, 'contact', $this->contact);
00231         }
00232 
00233         if ($this->reference !== null) {
00234             $reply->setArg(Auth_OpenID_OPENID_NS, 'reference',
00235                            $this->reference);
00236         }
00237 
00238         return $reply;
00239     }
00240 
00246     function whichEncoding()
00247     {
00248         global $_Auth_OpenID_Request_Modes;
00249 
00250         if ($this->hasReturnTo()) {
00251             if ($this->message->isOpenID2() &&
00252                 (strlen($this->encodeToURL()) >
00253                    Auth_OpenID_OPENID1_URL_LIMIT)) {
00254                 return Auth_OpenID_ENCODE_HTML_FORM;
00255             } else {
00256                 return Auth_OpenID_ENCODE_URL;
00257             }
00258         }
00259 
00260         if (!$this->message) {
00261             return null;
00262         }
00263 
00264         $mode = $this->message->getArg(Auth_OpenID_OPENID_NS,
00265                                        'mode');
00266 
00267         if ($mode) {
00268             if (!in_array($mode, $_Auth_OpenID_Request_Modes)) {
00269                 return Auth_OpenID_ENCODE_KVFORM;
00270             }
00271         }
00272         return null;
00273     }
00274 
00278     function toString()
00279     {
00280         if ($this->text) {
00281             return $this->text;
00282         } else {
00283             return get_class($this) . " error";
00284         }
00285     }
00286 }
00287 
00294 class Auth_OpenID_NoReturnToError extends Auth_OpenID_ServerError {
00295     function Auth_OpenID_NoReturnToError($message = null,
00296                                          $text = "No return_to URL available")
00297     {
00298         parent::Auth_OpenID_ServerError($message, $text);
00299     }
00300 
00301     function toString()
00302     {
00303         return "No return_to available";
00304     }
00305 }
00306 
00312 class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError {
00313     function Auth_OpenID_MalformedReturnURL($message, $return_to)
00314     {
00315         $this->return_to = $return_to;
00316         parent::Auth_OpenID_ServerError($message, "malformed return_to URL");
00317     }
00318 }
00319 
00325 class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError {
00326     function Auth_OpenID_MalformedTrustRoot($message = null,
00327                                             $text = "Malformed trust root")
00328     {
00329         parent::Auth_OpenID_ServerError($message, $text);
00330     }
00331 
00332     function toString()
00333     {
00334         return "Malformed trust root";
00335     }
00336 }
00337 
00343 class Auth_OpenID_Request {
00344     var $mode = null;
00345 }
00346 
00352 class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request {
00353     var $mode = "check_authentication";
00354     var $invalidate_handle = null;
00355 
00356     function Auth_OpenID_CheckAuthRequest($assoc_handle, $signed,
00357                                           $invalidate_handle = null)
00358     {
00359         $this->assoc_handle = $assoc_handle;
00360         $this->signed = $signed;
00361         if ($invalidate_handle !== null) {
00362             $this->invalidate_handle = $invalidate_handle;
00363         }
00364         $this->namespace = Auth_OpenID_OPENID2_NS;
00365         $this->message = null;
00366     }
00367 
00368     function fromMessage($message, $server=null)
00369     {
00370         $required_keys = array('assoc_handle', 'sig', 'signed');
00371 
00372         foreach ($required_keys as $k) {
00373             if (!$message->getArg(Auth_OpenID_OPENID_NS, $k)) {
00374                 return new Auth_OpenID_ServerError($message,
00375                     sprintf("%s request missing required parameter %s from \
00376                             query", "check_authentication", $k));
00377             }
00378         }
00379 
00380         $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, 'assoc_handle');
00381         $sig = $message->getArg(Auth_OpenID_OPENID_NS, 'sig');
00382 
00383         $signed_list = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
00384         $signed_list = explode(",", $signed_list);
00385 
00386         $signed = $message;
00387         if ($signed->hasKey(Auth_OpenID_OPENID_NS, 'mode')) {
00388             $signed->setArg(Auth_OpenID_OPENID_NS, 'mode', 'id_res');
00389         }
00390 
00391         $result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $signed);
00392         $result->message = $message;
00393         $result->sig = $sig;
00394         $result->invalidate_handle = $message->getArg(Auth_OpenID_OPENID_NS,
00395                                                       'invalidate_handle');
00396         return $result;
00397     }
00398 
00399     function answer(&$signatory)
00400     {
00401         $is_valid = $signatory->verify($this->assoc_handle, $this->signed);
00402 
00403         // Now invalidate that assoc_handle so it this checkAuth
00404         // message cannot be replayed.
00405         $signatory->invalidate($this->assoc_handle, true);
00406         $response = new Auth_OpenID_ServerResponse($this);
00407 
00408         $response->fields->setArg(Auth_OpenID_OPENID_NS,
00409                                   'is_valid',
00410                                   ($is_valid ? "true" : "false"));
00411 
00412         if ($this->invalidate_handle) {
00413             $assoc = $signatory->getAssociation($this->invalidate_handle,
00414                                                 false);
00415             if (!$assoc) {
00416                 $response->fields->setArg(Auth_OpenID_OPENID_NS,
00417                                           'invalidate_handle',
00418                                           $this->invalidate_handle);
00419             }
00420         }
00421         return $response;
00422     }
00423 }
00424 
00430 class Auth_OpenID_PlainTextServerSession {
00435     var $session_type = 'no-encryption';
00436     var $needs_math = false;
00437     var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256');
00438 
00439     function fromMessage($unused_request)
00440     {
00441         return new Auth_OpenID_PlainTextServerSession();
00442     }
00443 
00444     function answer($secret)
00445     {
00446         return array('mac_key' => base64_encode($secret));
00447     }
00448 }
00449 
00455 class Auth_OpenID_DiffieHellmanSHA1ServerSession {
00461     var $session_type = 'DH-SHA1';
00462     var $needs_math = true;
00463     var $allowed_assoc_types = array('HMAC-SHA1');
00464     var $hash_func = 'Auth_OpenID_SHA1';
00465 
00466     function Auth_OpenID_DiffieHellmanSHA1ServerSession($dh, $consumer_pubkey)
00467     {
00468         $this->dh = $dh;
00469         $this->consumer_pubkey = $consumer_pubkey;
00470     }
00471 
00472     function getDH($message)
00473     {
00474         $dh_modulus = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_modulus');
00475         $dh_gen = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_gen');
00476 
00477         if ((($dh_modulus === null) && ($dh_gen !== null)) ||
00478             (($dh_gen === null) && ($dh_modulus !== null))) {
00479 
00480             if ($dh_modulus === null) {
00481                 $missing = 'modulus';
00482             } else {
00483                 $missing = 'generator';
00484             }
00485 
00486             return new Auth_OpenID_ServerError($message,
00487                                 'If non-default modulus or generator is '.
00488                                 'supplied, both must be supplied.  Missing '.
00489                                 $missing);
00490         }
00491 
00492         $lib =& Auth_OpenID_getMathLib();
00493 
00494         if ($dh_modulus || $dh_gen) {
00495             $dh_modulus = $lib->base64ToLong($dh_modulus);
00496             $dh_gen = $lib->base64ToLong($dh_gen);
00497             if ($lib->cmp($dh_modulus, 0) == 0 ||
00498                 $lib->cmp($dh_gen, 0) == 0) {
00499                 return new Auth_OpenID_ServerError(
00500                   $message, "Failed to parse dh_mod or dh_gen");
00501             }
00502             $dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen);
00503         } else {
00504             $dh = new Auth_OpenID_DiffieHellman();
00505         }
00506 
00507         $consumer_pubkey = $message->getArg(Auth_OpenID_OPENID_NS,
00508                                             'dh_consumer_public');
00509         if ($consumer_pubkey === null) {
00510             return new Auth_OpenID_ServerError($message,
00511                                   'Public key for DH-SHA1 session '.
00512                                   'not found in query');
00513         }
00514 
00515         $consumer_pubkey =
00516             $lib->base64ToLong($consumer_pubkey);
00517 
00518         if ($consumer_pubkey === false) {
00519             return new Auth_OpenID_ServerError($message,
00520                                        "dh_consumer_public is not base64");
00521         }
00522 
00523         return array($dh, $consumer_pubkey);
00524     }
00525 
00526     function fromMessage($message)
00527     {
00528         $result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message);
00529 
00530         if (is_a($result, 'Auth_OpenID_ServerError')) {
00531             return $result;
00532         } else {
00533             list($dh, $consumer_pubkey) = $result;
00534             return new Auth_OpenID_DiffieHellmanSHA1ServerSession($dh,
00535                                                     $consumer_pubkey);
00536         }
00537     }
00538 
00539     function answer($secret)
00540     {
00541         $lib =& Auth_OpenID_getMathLib();
00542         $mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret,
00543                                         $this->hash_func);
00544         return array(
00545            'dh_server_public' =>
00546                 $lib->longToBase64($this->dh->public),
00547            'enc_mac_key' => base64_encode($mac_key));
00548     }
00549 }
00550 
00556 class Auth_OpenID_DiffieHellmanSHA256ServerSession
00557       extends Auth_OpenID_DiffieHellmanSHA1ServerSession {
00558 
00559     var $session_type = 'DH-SHA256';
00560     var $hash_func = 'Auth_OpenID_SHA256';
00561     var $allowed_assoc_types = array('HMAC-SHA256');
00562 
00563     function fromMessage($message)
00564     {
00565         $result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message);
00566 
00567         if (is_a($result, 'Auth_OpenID_ServerError')) {
00568             return $result;
00569         } else {
00570             list($dh, $consumer_pubkey) = $result;
00571             return new Auth_OpenID_DiffieHellmanSHA256ServerSession($dh,
00572                                                       $consumer_pubkey);
00573         }
00574     }
00575 }
00576 
00582 class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request {
00583     var $mode = "associate";
00584 
00585     function getSessionClasses()
00586     {
00587         return array(
00588           'no-encryption' => 'Auth_OpenID_PlainTextServerSession',
00589           'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ServerSession',
00590           'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ServerSession');
00591     }
00592 
00593     function Auth_OpenID_AssociateRequest(&$session, $assoc_type)
00594     {
00595         $this->session =& $session;
00596         $this->namespace = Auth_OpenID_OPENID2_NS;
00597         $this->assoc_type = $assoc_type;
00598     }
00599 
00600     function fromMessage($message, $server=null)
00601     {
00602         if ($message->isOpenID1()) {
00603             $session_type = $message->getArg(Auth_OpenID_OPENID_NS,
00604                                              'session_type');
00605 
00606             if ($session_type == 'no-encryption') {
00607                 // oidutil.log('Received OpenID 1 request with a no-encryption '
00608                 //             'assocaition session type. Continuing anyway.')
00609             } else if (!$session_type) {
00610                 $session_type = 'no-encryption';
00611             }
00612         } else {
00613             $session_type = $message->getArg(Auth_OpenID_OPENID_NS,
00614                                              'session_type');
00615             if ($session_type === null) {
00616                 return new Auth_OpenID_ServerError($message,
00617                   "session_type missing from request");
00618             }
00619         }
00620 
00621         $session_class = Auth_OpenID::arrayGet(
00622            Auth_OpenID_AssociateRequest::getSessionClasses(),
00623            $session_type);
00624 
00625         if ($session_class === null) {
00626             return new Auth_OpenID_ServerError($message,
00627                                                "Unknown session type " .
00628                                                $session_type);
00629         }
00630 
00631         $session = call_user_func(array($session_class, 'fromMessage'),
00632                                   $message);
00633         if (is_a($session, 'Auth_OpenID_ServerError')) {
00634             return $session;
00635         }
00636 
00637         $assoc_type = $message->getArg(Auth_OpenID_OPENID_NS,
00638                                        'assoc_type', 'HMAC-SHA1');
00639 
00640         if (!in_array($assoc_type, $session->allowed_assoc_types)) {
00641             $fmt = "Session type %s does not support association type %s";
00642             return new Auth_OpenID_ServerError($message,
00643               sprintf($fmt, $session_type, $assoc_type));
00644         }
00645 
00646         $obj = new Auth_OpenID_AssociateRequest($session, $assoc_type);
00647         $obj->message = $message;
00648         $obj->namespace = $message->getOpenIDNamespace();
00649         return $obj;
00650     }
00651 
00652     function answer($assoc)
00653     {
00654         $response = new Auth_OpenID_ServerResponse($this);
00655         $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
00656            array(
00657                  'expires_in' => sprintf('%d', $assoc->getExpiresIn()),
00658                  'assoc_type' => $this->assoc_type,
00659                  'assoc_handle' => $assoc->handle));
00660 
00661         $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
00662            $this->session->answer($assoc->secret));
00663 
00664         if (! ($this->session->session_type == 'no-encryption' 
00665                && $this->message->isOpenID1())) {
00666             $response->fields->setArg(Auth_OpenID_OPENID_NS,
00667                                       'session_type',
00668                                       $this->session->session_type);
00669         }
00670 
00671         return $response;
00672     }
00673 
00674     function answerUnsupported($text_message,
00675                                $preferred_association_type=null,
00676                                $preferred_session_type=null)
00677     {
00678         if ($this->message->isOpenID1()) {
00679             return new Auth_OpenID_ServerError($this->message);
00680         }
00681 
00682         $response = new Auth_OpenID_ServerResponse($this);
00683         $response->fields->setArg(Auth_OpenID_OPENID_NS,
00684                                   'error_code', 'unsupported-type');
00685         $response->fields->setArg(Auth_OpenID_OPENID_NS,
00686                                   'error', $text_message);
00687 
00688         if ($preferred_association_type) {
00689             $response->fields->setArg(Auth_OpenID_OPENID_NS,
00690                                       'assoc_type',
00691                                       $preferred_association_type);
00692         }
00693 
00694         if ($preferred_session_type) {
00695             $response->fields->setArg(Auth_OpenID_OPENID_NS,
00696                                       'session_type',
00697                                       $preferred_session_type);
00698         }
00699 
00700         return $response;
00701     }
00702 }
00703 
00709 class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
00714     var $verifyReturnTo = 'Auth_OpenID_verifyReturnTo';
00715 
00719     var $mode = "checkid_setup"; // or "checkid_immediate"
00720 
00724     var $immediate = false;
00725 
00729     var $trust_root = null;
00730 
00735     var $namespace;
00736     
00737     function make(&$message, $identity, $return_to, $trust_root = null,
00738                   $immediate = false, $assoc_handle = null, $server = null)
00739     {
00740         if ($server === null) {
00741             return new Auth_OpenID_ServerError($message,
00742                                                "server must not be null");
00743         }
00744 
00745         if ($return_to &&
00746             !Auth_OpenID_TrustRoot::_parse($return_to)) {
00747             return new Auth_OpenID_MalformedReturnURL($message, $return_to);
00748         }
00749 
00750         $r = new Auth_OpenID_CheckIDRequest($identity, $return_to,
00751                                             $trust_root, $immediate,
00752                                             $assoc_handle, $server);
00753 
00754         $r->namespace = $message->getOpenIDNamespace();
00755         $r->message =& $message;
00756 
00757         if (!$r->trustRootValid()) {
00758             return new Auth_OpenID_UntrustedReturnURL($message,
00759                                                       $return_to,
00760                                                       $trust_root);
00761         } else {
00762             return $r;
00763         }
00764     }
00765 
00766     function Auth_OpenID_CheckIDRequest($identity, $return_to,
00767                                         $trust_root = null, $immediate = false,
00768                                         $assoc_handle = null, $server = null,
00769                                         $claimed_id = null)
00770     {
00771         $this->namespace = Auth_OpenID_OPENID2_NS;
00772         $this->assoc_handle = $assoc_handle;
00773         $this->identity = $identity;
00774         if ($claimed_id === null) {
00775             $this->claimed_id = $identity;
00776         } else {
00777             $this->claimed_id = $claimed_id;
00778         }
00779         $this->return_to = $return_to;
00780         $this->trust_root = $trust_root;
00781         $this->server =& $server;
00782 
00783         if ($immediate) {
00784             $this->immediate = true;
00785             $this->mode = "checkid_immediate";
00786         } else {
00787             $this->immediate = false;
00788             $this->mode = "checkid_setup";
00789         }
00790     }
00791 
00792     function equals($other)
00793     {
00794         return (
00795                 (is_a($other, 'Auth_OpenID_CheckIDRequest')) &&
00796                 ($this->namespace == $other->namespace) &&
00797                 ($this->assoc_handle == $other->assoc_handle) &&
00798                 ($this->identity == $other->identity) &&
00799                 ($this->claimed_id == $other->claimed_id) &&
00800                 ($this->return_to == $other->return_to) &&
00801                 ($this->trust_root == $other->trust_root));
00802     }
00803 
00804     /*
00805      * Does the relying party publish the return_to URL for this
00806      * response under the realm? It is up to the provider to set a
00807      * policy for what kinds of realms should be allowed. This
00808      * return_to URL verification reduces vulnerability to data-theft
00809      * attacks based on open proxies, corss-site-scripting, or open
00810      * redirectors.
00811      *
00812      * This check should only be performed after making sure that the
00813      * return_to URL matches the realm.
00814      *
00815      * @return true if the realm publishes a document with the
00816      * return_to URL listed, false if not or if discovery fails
00817      */
00818     function returnToVerified()
00819     {
00820         return call_user_func_array($this->verifyReturnTo,
00821                                     array($this->trust_root, $this->return_to));
00822     }
00823 
00824     function fromMessage(&$message, $server)
00825     {
00826         $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
00827         $immediate = null;
00828 
00829         if ($mode == "checkid_immediate") {
00830             $immediate = true;
00831             $mode = "checkid_immediate";
00832         } else {
00833             $immediate = false;
00834             $mode = "checkid_setup";
00835         }
00836 
00837         $return_to = $message->getArg(Auth_OpenID_OPENID_NS,
00838                                       'return_to');
00839 
00840         if (($message->isOpenID1()) &&
00841             (!$return_to)) {
00842             $fmt = "Missing required field 'return_to' from checkid request";
00843             return new Auth_OpenID_ServerError($message, $fmt);
00844         }
00845 
00846         $identity = $message->getArg(Auth_OpenID_OPENID_NS,
00847                                      'identity');
00848         $claimed_id = $message->getArg(Auth_OpenID_OPENID_NS, 'claimed_id');
00849         if ($message->isOpenID1()) {
00850             if ($identity === null) {
00851                 $s = "OpenID 1 message did not contain openid.identity";
00852                 return new Auth_OpenID_ServerError($message, $s);
00853             }
00854         } else {
00855             if ($identity && !$claimed_id) {
00856                 $s = "OpenID 2.0 message contained openid.identity but not " .
00857                   "claimed_id";
00858                 return new Auth_OpenID_ServerError($message, $s);
00859             } else if ($claimed_id && !$identity) {
00860                 $s = "OpenID 2.0 message contained openid.claimed_id " .
00861                   "but not identity";
00862                 return new Auth_OpenID_ServerError($message, $s);
00863             }
00864         }
00865 
00866         // There's a case for making self.trust_root be a TrustRoot
00867         // here.  But if TrustRoot isn't currently part of the
00868         // "public" API, I'm not sure it's worth doing.
00869         if ($message->isOpenID1()) {
00870             $trust_root_param = 'trust_root';
00871         } else {
00872             $trust_root_param = 'realm';
00873         }
00874         $trust_root = $message->getArg(Auth_OpenID_OPENID_NS, 
00875                                        $trust_root_param);
00876         if (! $trust_root) {
00877             $trust_root = $return_to;
00878         }
00879 
00880         if (! $message->isOpenID1() && 
00881             ($return_to === null) &&
00882             ($trust_root === null)) {
00883             return new Auth_OpenID_ServerError($message,
00884               "openid.realm required when openid.return_to absent");
00885         }
00886 
00887         $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
00888                                          'assoc_handle');
00889 
00890         $obj = Auth_OpenID_CheckIDRequest::make($message,
00891                                                 $identity,
00892                                                 $return_to,
00893                                                 $trust_root,
00894                                                 $immediate,
00895                                                 $assoc_handle,
00896                                                 $server);
00897 
00898         if (is_a($obj, 'Auth_OpenID_ServerError')) {
00899             return $obj;
00900         }
00901 
00902         $obj->claimed_id = $claimed_id;
00903 
00904         return $obj;
00905     }
00906 
00907     function idSelect()
00908     {
00909         // Is the identifier to be selected by the IDP?
00910         // So IDPs don't have to import the constant
00911         return $this->identity == Auth_OpenID_IDENTIFIER_SELECT;
00912     }
00913 
00914     function trustRootValid()
00915     {
00916         if (!$this->trust_root) {
00917             return true;
00918         }
00919 
00920         $tr = Auth_OpenID_TrustRoot::_parse($this->trust_root);
00921         if ($tr === false) {
00922             return new Auth_OpenID_MalformedTrustRoot($this->message,
00923                                                       $this->trust_root);
00924         }
00925 
00926         if ($this->return_to !== null) {
00927             return Auth_OpenID_TrustRoot::match($this->trust_root,
00928                                                 $this->return_to);
00929         } else {
00930             return true;
00931         }
00932     }
00933 
00973     function answer($allow, $server_url = null, $identity = null,
00974                     $claimed_id = null)
00975     {
00976         if (!$this->return_to) {
00977             return new Auth_OpenID_NoReturnToError();
00978         }
00979 
00980         if (!$server_url) {
00981             if ((!$this->message->isOpenID1()) &&
00982                 (!$this->server->op_endpoint)) {
00983                 return new Auth_OpenID_ServerError(null,
00984                   "server should be constructed with op_endpoint to " .
00985                   "respond to OpenID 2.0 messages.");
00986             }
00987 
00988             $server_url = $this->server->op_endpoint;
00989         }
00990 
00991         if ($allow) {
00992             $mode = 'id_res';
00993         } else if ($this->message->isOpenID1()) {
00994             if ($this->immediate) {
00995                 $mode = 'id_res';
00996             } else {
00997                 $mode = 'cancel';
00998             }
00999         } else {
01000             if ($this->immediate) {
01001                 $mode = 'setup_needed';
01002             } else {
01003                 $mode = 'cancel';
01004             }
01005         }
01006 
01007         if (!$this->trustRootValid()) {
01008             return new Auth_OpenID_UntrustedReturnURL(null,
01009                                                       $this->return_to,
01010                                                       $this->trust_root);
01011         }
01012 
01013         $response = new Auth_OpenID_ServerResponse($this);
01014 
01015         if ($claimed_id &&
01016             ($this->message->isOpenID1())) {
01017             return new Auth_OpenID_ServerError(null,
01018               "claimed_id is new in OpenID 2.0 and not " .
01019               "available for ".$this->namespace);
01020         }
01021 
01022         if ($identity && !$claimed_id) {
01023             $claimed_id = $identity;
01024         }
01025 
01026         if ($allow) {
01027 
01028             if ($this->identity == Auth_OpenID_IDENTIFIER_SELECT) {
01029                 if (!$identity) {
01030                     return new Auth_OpenID_ServerError(null,
01031                       "This request uses IdP-driven identifier selection.  " .
01032                       "You must supply an identifier in the response.");
01033                 }
01034 
01035                 $response_identity = $identity;
01036                 $response_claimed_id = $claimed_id;
01037 
01038             } else if ($this->identity) {
01039                 if ($identity &&
01040                     ($this->identity != $identity)) {
01041                     $fmt = "Request was for %s, cannot reply with identity %s";
01042                     return new Auth_OpenID_ServerError(null,
01043                       sprintf($fmt, $this->identity, $identity));
01044                 }
01045 
01046                 $response_identity = $this->identity;
01047                 $response_claimed_id = $this->claimed_id;
01048             } else {
01049                 if ($identity) {
01050                     return new Auth_OpenID_ServerError(null,
01051                       "This request specified no identity and " .
01052                       "you supplied ".$identity);
01053                 }
01054 
01055                 $response_identity = null;
01056             }
01057 
01058             if (($this->message->isOpenID1()) &&
01059                 ($response_identity === null)) {
01060                 return new Auth_OpenID_ServerError(null,
01061                   "Request was an OpenID 1 request, so response must " .
01062                   "include an identifier.");
01063             }
01064 
01065             $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
01066                    array('mode' => $mode,
01067                          'return_to' => $this->return_to,
01068                          'response_nonce' => Auth_OpenID_mkNonce()));
01069 
01070             if (!$this->message->isOpenID1()) {
01071                 $response->fields->setArg(Auth_OpenID_OPENID_NS,
01072                                           'op_endpoint', $server_url);
01073             }
01074 
01075             if ($response_identity !== null) {
01076                 $response->fields->setArg(
01077                                           Auth_OpenID_OPENID_NS,
01078                                           'identity',
01079                                           $response_identity);
01080                 if ($this->message->isOpenID2()) {
01081                     $response->fields->setArg(
01082                                               Auth_OpenID_OPENID_NS,
01083                                               'claimed_id',
01084                                               $response_claimed_id);
01085                 }
01086             }
01087 
01088         } else {
01089             $response->fields->setArg(Auth_OpenID_OPENID_NS,
01090                                       'mode', $mode);
01091 
01092             if ($this->immediate) {
01093                 if (($this->message->isOpenID1()) &&
01094                     (!$server_url)) {
01095                     return new Auth_OpenID_ServerError(null,
01096                                  'setup_url is required for $allow=false \
01097                                   in OpenID 1.x immediate mode.');
01098                 }
01099 
01100                 $setup_request =& new Auth_OpenID_CheckIDRequest(
01101                                                 $this->identity,
01102                                                 $this->return_to,
01103                                                 $this->trust_root,
01104                                                 false,
01105                                                 $this->assoc_handle,
01106                                                 $this->server,
01107                                                 $this->claimed_id);
01108                 $setup_request->message = $this->message;
01109 
01110                 $setup_url = $setup_request->encodeToURL($server_url);
01111 
01112                 if ($setup_url === null) {
01113                     return new Auth_OpenID_NoReturnToError();
01114                 }
01115 
01116                 $response->fields->setArg(Auth_OpenID_OPENID_NS,
01117                                           'user_setup_url',
01118                                           $setup_url);
01119             }
01120         }
01121 
01122         return $response;
01123     }
01124 
01125     function encodeToURL($server_url)
01126     {
01127         if (!$this->return_to) {
01128             return new Auth_OpenID_NoReturnToError();
01129         }
01130 
01131         // Imported from the alternate reality where these classes are
01132         // used in both the client and server code, so Requests are
01133         // Encodable too.  That's right, code imported from alternate
01134         // realities all for the love of you, id_res/user_setup_url.
01135 
01136         $q = array('mode' => $this->mode,
01137                    'identity' => $this->identity,
01138                    'claimed_id' => $this->claimed_id,
01139                    'return_to' => $this->return_to);
01140 
01141         if ($this->trust_root) {
01142             if ($this->message->isOpenID1()) {
01143                 $q['trust_root'] = $this->trust_root;
01144             } else {
01145                 $q['realm'] = $this->trust_root;
01146             }
01147         }
01148 
01149         if ($this->assoc_handle) {
01150             $q['assoc_handle'] = $this->assoc_handle;
01151         }
01152 
01153         $response = new Auth_OpenID_Message(
01154             $this->message->getOpenIDNamespace());
01155         $response->updateArgs(Auth_OpenID_OPENID_NS, $q);
01156         return $response->toURL($server_url);
01157     }
01158 
01159     function getCancelURL()
01160     {
01161         if (!$this->return_to) {
01162             return new Auth_OpenID_NoReturnToError();
01163         }
01164 
01165         if ($this->immediate) {
01166             return new Auth_OpenID_ServerError(null,
01167                                                "Cancel is not an appropriate \
01168                                                response to immediate mode \
01169                                                requests.");
01170         }
01171 
01172         $response = new Auth_OpenID_Message(
01173             $this->message->getOpenIDNamespace());
01174         $response->setArg(Auth_OpenID_OPENID_NS, 'mode', 'cancel');
01175         return $response->toURL($this->return_to);
01176     }
01177 }
01178 
01184 class Auth_OpenID_ServerResponse {
01185 
01186     function Auth_OpenID_ServerResponse(&$request)
01187     {
01188         $this->request =& $request;
01189         $this->fields = new Auth_OpenID_Message($this->request->namespace);
01190     }
01191 
01192     function whichEncoding()
01193     {
01194       global $_Auth_OpenID_Request_Modes;
01195 
01196         if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) {
01197             if ($this->fields->isOpenID2() &&
01198                 (strlen($this->encodeToURL()) >
01199                    Auth_OpenID_OPENID1_URL_LIMIT)) {
01200                 return Auth_OpenID_ENCODE_HTML_FORM;
01201             } else {
01202                 return Auth_OpenID_ENCODE_URL;
01203             }
01204         } else {
01205             return Auth_OpenID_ENCODE_KVFORM;
01206         }
01207     }
01208 
01209     /*
01210      * Returns the form markup for this response.
01211      *
01212      * @return str
01213      */
01214     function toFormMarkup($form_tag_attrs=null)
01215     {
01216         return $this->fields->toFormMarkup($this->request->return_to,
01217                                            $form_tag_attrs);
01218     }
01219 
01220     /*
01221      * Returns an HTML document containing the form markup for this
01222      * response that autosubmits with javascript.
01223      */
01224     function toHTML()
01225     {
01226         return Auth_OpenID::autoSubmitHTML($this->toFormMarkup());
01227     }
01228 
01229     /*
01230      * Returns True if this response's encoding is ENCODE_HTML_FORM.
01231      * Convenience method for server authors.
01232      *
01233      * @return bool
01234      */
01235     function renderAsForm()
01236     {
01237         return $this->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM;
01238     }
01239 
01240 
01241     function encodeToURL()
01242     {
01243         return $this->fields->toURL($this->request->return_to);
01244     }
01245 
01246     function addExtension($extension_response)
01247     {
01248         $extension_response->toMessage($this->fields);
01249     }
01250 
01251     function needsSigning()
01252     {
01253         return $this->fields->getArg(Auth_OpenID_OPENID_NS,
01254                                      'mode') == 'id_res';
01255     }
01256 
01257     function encodeToKVForm()
01258     {
01259         return $this->fields->toKVForm();
01260     }
01261 }
01262 
01269 class Auth_OpenID_WebResponse {
01270     var $code = AUTH_OPENID_HTTP_OK;
01271     var $body = "";
01272 
01273     function Auth_OpenID_WebResponse($code = null, $headers = null,
01274                                      $body = null)
01275     {
01276         if ($code) {
01277             $this->code = $code;
01278         }
01279 
01280         if ($headers !== null) {
01281             $this->headers = $headers;
01282         } else {
01283             $this->headers = array();
01284         }
01285 
01286         if ($body !== null) {
01287             $this->body = $body;
01288         }
01289     }
01290 }
01291 
01298 class Auth_OpenID_Signatory {
01299 
01300     // = 14 * 24 * 60 * 60; # 14 days, in seconds
01301     var $SECRET_LIFETIME = 1209600;
01302 
01303     // keys have a bogus server URL in them because the filestore
01304     // really does expect that key to be a URL.  This seems a little
01305     // silly for the server store, since I expect there to be only one
01306     // server URL.
01307     var $normal_key = 'http://localhost/|normal';
01308     var $dumb_key = 'http://localhost/|dumb';
01309 
01313     function Auth_OpenID_Signatory(&$store)
01314     {
01315         // assert store is not None
01316         $this->store =& $store;
01317     }
01318 
01323     function verify($assoc_handle, $message)
01324     {
01325         $assoc = $this->getAssociation($assoc_handle, true);
01326         if (!$assoc) {
01327             // oidutil.log("failed to get assoc with handle %r to verify sig %r"
01328             //             % (assoc_handle, sig))
01329             return false;
01330         }
01331 
01332         return $assoc->checkMessageSignature($message);
01333     }
01334 
01339     function sign($response)
01340     {
01341         $signed_response = $response;
01342         $assoc_handle = $response->request->assoc_handle;
01343 
01344         if ($assoc_handle) {
01345             // normal mode
01346             $assoc = $this->getAssociation($assoc_handle, false, false);
01347             if (!$assoc || ($assoc->getExpiresIn() <= 0)) {
01348                 // fall back to dumb mode
01349                 $signed_response->fields->setArg(Auth_OpenID_OPENID_NS,
01350                              'invalidate_handle', $assoc_handle);
01351                 $assoc_type = ($assoc ? $assoc->assoc_type : 'HMAC-SHA1');
01352 
01353                 if ($assoc && ($assoc->getExpiresIn() <= 0)) {
01354                     $this->invalidate($assoc_handle, false);
01355                 }
01356 
01357                 $assoc = $this->createAssociation(true, $assoc_type);
01358             }
01359         } else {
01360             // dumb mode.
01361             $assoc = $this->createAssociation(true);
01362         }
01363 
01364         $signed_response->fields = $assoc->signMessage(
01365                                       $signed_response->fields);
01366         return $signed_response;
01367     }
01368 
01372     function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1')
01373     {
01374         $secret = Auth_OpenID_CryptUtil::getBytes(
01375                     Auth_OpenID_getSecretSize($assoc_type));
01376 
01377         $uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4));
01378         $handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq);
01379 
01380         $assoc = Auth_OpenID_Association::fromExpiresIn(
01381                       $this->SECRET_LIFETIME, $handle, $secret, $assoc_type);
01382 
01383         if ($dumb) {
01384             $key = $this->dumb_key;
01385         } else {
01386             $key = $this->normal_key;
01387         }
01388 
01389         $this->store->storeAssociation($key, $assoc);
01390         return $assoc;
01391     }
01392 
01397     function getAssociation($assoc_handle, $dumb, $check_expiration=true)
01398     {
01399         if ($assoc_handle === null) {
01400             return new Auth_OpenID_ServerError(null,
01401                                      "assoc_handle must not be null");
01402         }
01403 
01404         if ($dumb) {
01405             $key = $this->dumb_key;
01406         } else {
01407             $key = $this->normal_key;
01408         }
01409 
01410         $assoc = $this->store->getAssociation($key, $assoc_handle);
01411 
01412         if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) {
01413             if ($check_expiration) {
01414                 $this->store->removeAssociation($key, $assoc_handle);
01415                 $assoc = null;
01416             }
01417         }
01418 
01419         return $assoc;
01420     }
01421 
01425     function invalidate($assoc_handle, $dumb)
01426     {
01427         if ($dumb) {
01428             $key = $this->dumb_key;
01429         } else {
01430             $key = $this->normal_key;
01431         }
01432         $this->store->removeAssociation($key, $assoc_handle);
01433     }
01434 }
01435 
01442 class Auth_OpenID_Encoder {
01443 
01444     var $responseFactory = 'Auth_OpenID_WebResponse';
01445 
01450     function encode(&$response)
01451     {
01452         $cls = $this->responseFactory;
01453 
01454         $encode_as = $response->whichEncoding();
01455         if ($encode_as == Auth_OpenID_ENCODE_KVFORM) {
01456             $wr = new $cls(null, null, $response->encodeToKVForm());
01457             if (is_a($response, 'Auth_OpenID_ServerError')) {
01458                 $wr->code = AUTH_OPENID_HTTP_ERROR;
01459             }
01460         } else if ($encode_as == Auth_OpenID_ENCODE_URL) {
01461             $location = $response->encodeToURL();
01462             $wr = new $cls(AUTH_OPENID_HTTP_REDIRECT,
01463                            array('location' => $location));
01464         } else if ($encode_as == Auth_OpenID_ENCODE_HTML_FORM) {
01465           $wr = new $cls(AUTH_OPENID_HTTP_OK, array(),
01466                          $response->toFormMarkup());
01467         } else {
01468             return new Auth_OpenID_EncodingError($response);
01469         }
01470         return $wr;
01471     }
01472 }
01473 
01479 class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder {
01480 
01481     function Auth_OpenID_SigningEncoder(&$signatory)
01482     {
01483         $this->signatory =& $signatory;
01484     }
01485 
01490     function encode(&$response)
01491     {
01492         // the isinstance is a bit of a kludge... it means there isn't
01493         // really an adapter to make the interfaces quite match.
01494         if (!is_a($response, 'Auth_OpenID_ServerError') &&
01495             $response->needsSigning()) {
01496 
01497             if (!$this->signatory) {
01498                 return new Auth_OpenID_ServerError(null,
01499                                        "Must have a store to sign request");
01500             }
01501 
01502             if ($response->fields->hasKey(Auth_OpenID_OPENID_NS, 'sig')) {
01503                 return new Auth_OpenID_AlreadySigned($response);
01504             }
01505             $response = $this->signatory->sign($response);
01506         }
01507 
01508         return parent::encode($response);
01509     }
01510 }
01511 
01517 class Auth_OpenID_Decoder {
01518 
01519     function Auth_OpenID_Decoder(&$server)
01520     {
01521         $this->server =& $server;
01522 
01523         $this->handlers = array(
01524             'checkid_setup' => 'Auth_OpenID_CheckIDRequest',
01525             'checkid_immediate' => 'Auth_OpenID_CheckIDRequest',
01526             'check_authentication' => 'Auth_OpenID_CheckAuthRequest',
01527             'associate' => 'Auth_OpenID_AssociateRequest'
01528             );
01529     }
01530 
01535     function decode($query)
01536     {
01537         if (!$query) {
01538             return null;
01539         }
01540 
01541         $message = Auth_OpenID_Message::fromPostArgs($query);
01542 
01543         if ($message === null) {
01544             /*
01545              * It's useful to have a Message attached to a
01546              * ProtocolError, so we override the bad ns value to build
01547              * a Message out of it.  Kinda kludgy, since it's made of
01548              * lies, but the parts that aren't lies are more useful
01549              * than a 'None'.
01550              */
01551             $old_ns = $query['openid.ns'];
01552 
01553             $query['openid.ns'] = Auth_OpenID_OPENID2_NS;
01554             $message = Auth_OpenID_Message::fromPostArgs($query);
01555             return new Auth_OpenID_ServerError(
01556                   $message,
01557                   sprintf("Invalid OpenID namespace URI: %s", $old_ns));
01558         }
01559 
01560         $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
01561         if (!$mode) {
01562             return new Auth_OpenID_ServerError($message,
01563                                                "No mode value in message");
01564         }
01565 
01566         if (Auth_OpenID::isFailure($mode)) {
01567             return new Auth_OpenID_ServerError($message,
01568                                                $mode->message);
01569         }
01570 
01571         $handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode,
01572                                             $this->defaultDecoder($message));
01573 
01574         if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) {
01575             return call_user_func_array(array($handlerCls, 'fromMessage'),
01576                                         array($message, $this->server));
01577         } else {
01578             return $handlerCls;
01579         }
01580     }
01581 
01582     function defaultDecoder($message)
01583     {
01584         $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
01585 
01586         if (Auth_OpenID::isFailure($mode)) {
01587             return new Auth_OpenID_ServerError($message,
01588                                                $mode->message);
01589         }
01590 
01591         return new Auth_OpenID_ServerError($message,
01592                        sprintf("Unrecognized OpenID mode %s", $mode));
01593     }
01594 }
01595 
01601 class Auth_OpenID_EncodingError {
01602     function Auth_OpenID_EncodingError(&$response)
01603     {
01604         $this->response =& $response;
01605     }
01606 }
01607 
01613 class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError {
01614     // This response is already signed.
01615 }
01616 
01623 class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError {
01624     function Auth_OpenID_UntrustedReturnURL($message, $return_to,
01625                                             $trust_root)
01626     {
01627         parent::Auth_OpenID_ServerError($message, "Untrusted return_to URL");
01628         $this->return_to = $return_to;
01629         $this->trust_root = $trust_root;
01630     }
01631 
01632     function toString()
01633     {
01634         return sprintf("return_to %s not under trust_root %s",
01635                        $this->return_to, $this->trust_root);
01636     }
01637 }
01638 
01676 class Auth_OpenID_Server {
01677     function Auth_OpenID_Server(&$store, $op_endpoint=null)
01678     {
01679         $this->store =& $store;
01680         $this->signatory =& new Auth_OpenID_Signatory($this->store);
01681         $this->encoder =& new Auth_OpenID_SigningEncoder($this->signatory);
01682         $this->decoder =& new Auth_OpenID_Decoder($this);
01683         $this->op_endpoint = $op_endpoint;
01684         $this->negotiator =& Auth_OpenID_getDefaultNegotiator();
01685     }
01686 
01698     function handleRequest($request)
01699     {
01700         if (method_exists($this, "openid_" . $request->mode)) {
01701             $handler = array($this, "openid_" . $request->mode);
01702             return call_user_func($handler, $request);
01703         }
01704         return null;
01705     }
01706 
01710     function openid_check_authentication(&$request)
01711     {
01712         return $request->answer($this->signatory);
01713     }
01714 
01718     function openid_associate(&$request)
01719     {
01720         $assoc_type = $request->assoc_type;
01721         $session_type = $request->session->session_type;
01722         if ($this->negotiator->isAllowed($assoc_type, $session_type)) {
01723             $assoc = $this->signatory->createAssociation(false,
01724                                                          $assoc_type);
01725             return $request->answer($assoc);
01726         } else {
01727             $message = sprintf('Association type %s is not supported with '.
01728                                'session type %s', $assoc_type, $session_type);
01729             list($preferred_assoc_type, $preferred_session_type) =
01730                 $this->negotiator->getAllowedType();
01731             return $request->answerUnsupported($message,
01732                                                $preferred_assoc_type,
01733                                                $preferred_session_type);
01734         }
01735     }
01736 
01741     function encodeResponse(&$response)
01742     {
01743         return $this->encoder->encode($response);
01744     }
01745 
01750     function decodeRequest($query=null)
01751     {
01752         if ($query === null) {
01753             $query = Auth_OpenID::getQuery();
01754         }
01755 
01756         return $this->decoder->decode($query);
01757     }
01758 }
01759 
01760 ?>

Generated on Thu Feb 19 15:02:21 2009 for OXID eShop CE by  doxygen 1.5.5