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
00223
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
00404
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
00608
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";
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
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
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
00867
00868
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
00910
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
01132
01133
01134
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
01211
01212
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
01222
01223
01224 function toHTML()
01225 {
01226 return Auth_OpenID::autoSubmitHTML($this->toFormMarkup());
01227 }
01228
01229
01230
01231
01232
01233
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
01301 var $SECRET_LIFETIME = 1209600;
01302
01303
01304
01305
01306
01307 var $normal_key = 'http://localhost/|normal';
01308 var $dumb_key = 'http://localhost/|dumb';
01309
01313 function Auth_OpenID_Signatory(&$store)
01314 {
01315
01316 $this->store =& $store;
01317 }
01318
01323 function verify($assoc_handle, $message)
01324 {
01325 $assoc = $this->getAssociation($assoc_handle, true);
01326 if (!$assoc) {
01327
01328
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
01346 $assoc = $this->getAssociation($assoc_handle, false, false);
01347 if (!$assoc || ($assoc->getExpiresIn() <= 0)) {
01348
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
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
01493
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
01546
01547
01548
01549
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
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 ?>