summaryrefslogtreecommitdiff
path: root/modules/openid
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2010-03-22 18:48:20 +0000
committerDries Buytaert <dries@buytaert.net>2010-03-22 18:48:20 +0000
commite8d18e41fc25af8dad5746e5b3bd6982c2492c2e (patch)
treeea2f2bf94b63828003376e9d4ad3c5e18a27e474 /modules/openid
parent2598778efa1887a4f2a8eb016114d32e8b6259a8 (diff)
downloadbrdo-e8d18e41fc25af8dad5746e5b3bd6982c2492c2e.tar.gz
brdo-e8d18e41fc25af8dad5746e5b3bd6982c2492c2e.tar.bz2
- Patch #218097 by c960657: OpenID must use canonical ID when authenticating XRI i-names.
Diffstat (limited to 'modules/openid')
-rw-r--r--modules/openid/openid.inc59
-rw-r--r--modules/openid/openid.module53
-rw-r--r--modules/openid/openid.test3
-rw-r--r--modules/openid/tests/openid_test.module21
-rw-r--r--modules/openid/xrds.inc90
5 files changed, 114 insertions, 112 deletions
diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc
index a1f3b90b4..72e931718 100644
--- a/modules/openid/openid.inc
+++ b/modules/openid/openid.inc
@@ -61,6 +61,16 @@ define('OPENID_NS_SREG', 'http://openid.net/extensions/sreg/1.1');
define('OPENID_NS_AX', 'http://openid.net/srv/ax/1.0');
/**
+ * Extensible Resource Descriptor documents.
+ */
+define('OPENID_NS_XRD', 'xri://$xrd*($v*2.0)');
+
+/**
+ * OpenID IDP for Google hosted domains.
+ */
+define('OPENID_NS_GOOGLE', 'http://namespace.google.com/openid/xmlns');
+
+/**
* Performs an HTTP 302 redirect (for the 1.x protocol).
*/
function openid_redirect_http($url, $message) {
@@ -109,6 +119,49 @@ function openid_redirect_form($form, &$form_state, $url, $message) {
}
/**
+ * Parse an XRDS document.
+ *
+ * @param $raw_xml
+ * A string containing the XRDS document.
+ * @return
+ * An array of service entries.
+ */
+function _openid_xrds_parse($raw_xml) {
+ $services = array();
+ try {
+ $xml = @new SimpleXMLElement($raw_xml);
+ foreach ($xml->children(OPENID_NS_XRD)->XRD as $xrd) {
+ foreach ($xrd->children(OPENID_NS_XRD)->Service as $service_element) {
+ $service = array(
+ 'priority' => $service_element->attributes()->priority ? (int)$service_element->attributes()->priority : PHP_INT_MAX,
+ 'types' => array(),
+ 'uri' => (string)$service_element->children(OPENID_NS_XRD)->URI,
+ 'service' => $service_element,
+ 'xrd' => $xrd,
+ );
+ foreach ($service_element->Type as $type) {
+ $service['types'][] = (string)$type;
+ }
+ if ($service_element->children(OPENID_NS_XRD)->Delegate) {
+ $service['identity'] = (string)$service_element->children(OPENID_NS_XRD)->Delegate;
+ }
+ if ($service_element->children(OPENID_NS_XRD)->LocalID) {
+ $service['identity'] = (string)$service_element->children(OPENID_NS_XRD)->LocalID;
+ }
+ else {
+ $service['identity'] = FALSE;
+ }
+ $services[] = $service;
+ }
+ }
+ }
+ catch (Exception $e) {
+ // Invalid XML.
+ }
+ return $services;
+}
+
+/**
* Select a service element.
*
* The procedure is described in OpenID Authentication 2.0, section 7.3.2.
@@ -155,6 +208,12 @@ function _openid_select_service(array $services) {
}
}
+ if ($selected_service) {
+ // Unset SimpleXMLElement instances that cannot be saved in $_SESSION.
+ unset($selected_service['xrd']);
+ unset($selected_service['service']);
+ }
+
return $selected_service;
}
diff --git a/modules/openid/openid.module b/modules/openid/openid.module
index e270006a2..6d8cbfbdf 100644
--- a/modules/openid/openid.module
+++ b/modules/openid/openid.module
@@ -208,16 +208,12 @@ function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
$claimed_id = $identity = 'http://specs.openid.net/auth/2.0/identifier_select';
}
else {
- // Look for OP-Local Identifier.
- if (!empty($service['localid'])) {
- $identity = $service['localid'];
- }
- elseif (!empty($service['delegate'])) {
- $identity = $service['delegate'];
- }
- else {
- $identity = $claimed_id;
+ // Use Claimed ID and/or OP-Local Identifier from service description, if
+ // available.
+ if (!empty($service['claimed_id'])) {
+ $claimed_id = $service['claimed_id'];
}
+ $identity = !empty($service['identity']) ? $service['identity'] : $claimed_id;
}
$request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $service);
@@ -258,12 +254,21 @@ function openid_complete($response = array()) {
}
else {
if (openid_verify_assertion($service['uri'], $response)) {
+ // OpenID Authentication, section 7.3.2.3 and Appendix A.5:
+ // The CanonicalID specified in the XRDS document must be used as the
+ // account key. We rely on the XRI proxy resolver to verify that the
+ // provider is authorized to respond on behalf of the specified
+ // identifer (required per Extensible Resource Identifier (XRI)
+ // (XRI) Resolution Version 2.0, section 14.3):
+ if (!empty($service['claimed_id'])) {
+ $response['openid.claimed_id'] = $service['claimed_id'];
+ }
// OpenID Authentication, section 11.2:
// If the returned Claimed Identifier is different from the one sent
// to the OpenID Provider, we need to do discovery on the returned
- // identififer to make sure that the provider is authorized to respond
+ // identifier to make sure that the provider is authorized to respond
// on behalf of this.
- if ($service['version'] == 2 && $response['openid.claimed_id'] != openid_normalize($claimed_id)) {
+ elseif ($service['version'] == 2 && $response['openid.claimed_id'] != openid_normalize($claimed_id)) {
$services = openid_discovery($response['openid.claimed_id']);
$uris = array();
foreach ($services as $discovered_service) {
@@ -338,8 +343,14 @@ function openid_openid_discovery_method_info() {
*/
function _openid_xri_discovery($claimed_id) {
if (_openid_is_xri($claimed_id)) {
- $xrds_url = 'http://xri.net/' . $claimed_id;
- _openid_xrds_discovery($xrds_url);
+ // Resolve XRI using a proxy resolver (Extensible Resource Identifier (XRI)
+ // Resolution Version 2.0, section 11.2).
+ $xrds_url = variable_get('xri_proxy_resolver', 'http://xri.net/') . rawurlencode($claimed_id) . '?_xrd_r=application/xrds+xml';
+ $services = _openid_xrds_discovery($xrds_url);
+ foreach ($services as &$service) {
+ $service['claimed_id'] = openid_normalize((string)$service['xrd']->children(OPENID_NS_XRD)->CanonicalID);
+ }
+ return $services;
}
}
@@ -362,7 +373,7 @@ function _openid_xrds_discovery($claimed_id) {
if (!isset($result->error)) {
if (isset($result->headers['Content-Type']) && preg_match("/application\/xrds\+xml/", $result->headers['Content-Type'])) {
// Parse XML document to find URL
- $services = xrds_parse($result->data);
+ $services = _openid_xrds_parse($result->data);
}
else {
$xrds_url = NULL;
@@ -377,7 +388,7 @@ function _openid_xrds_discovery($claimed_id) {
$headers = array('Accept' => 'application/xrds+xml');
$xrds_result = drupal_http_request($xrds_url, array('headers' => $headers));
if (!isset($xrds_result->error)) {
- $services = xrds_parse($xrds_result->data);
+ $services = _openid_xrds_parse($xrds_result->data);
}
}
}
@@ -386,19 +397,19 @@ function _openid_xrds_discovery($claimed_id) {
if (count($services) == 0) {
// Look for 2.0 links
$uri = _openid_link_href('openid2.provider', $result->data);
- $delegate = _openid_link_href('openid2.local_id', $result->data);
+ $identity = _openid_link_href('openid2.local_id', $result->data);
$type = 'http://specs.openid.net/auth/2.0/signon';
// 1.x links
if (empty($uri)) {
$uri = _openid_link_href('openid.server', $result->data);
- $delegate = _openid_link_href('openid.delegate', $result->data);
+ $identity = _openid_link_href('openid.delegate', $result->data);
$type = 'http://openid.net/signon/1.1';
}
if (!empty($uri)) {
$services[] = array(
'uri' => $uri,
- 'delegate' => $delegate,
+ 'identity' => $identity,
'types' => array($type),
);
}
@@ -432,9 +443,9 @@ function _openid_google_user_discovery($claimed_id) {
$xrds_url = $matches[1];
$services = _openid_xrds_discovery($xrds_url);
- foreach ($services as $service) {
- if (in_array('http://www.iana.org/assignments/relation/describedby', $service['types']) && !empty($service['additional']['URITEMPLATE'])) {
- $template = $service['additional']['URITEMPLATE'];
+ foreach ($services as $i => $service) {
+ if (in_array('http://www.iana.org/assignments/relation/describedby', $service['types']) && $service['service']->children(OPENID_NS_GOOGLE)->URITemplate) {
+ $template = (string)$service['service']->children(OPENID_NS_GOOGLE)->URITemplate;
$xrds_url = str_replace('{%uri}', rawurlencode($claimed_id), $template);
return _openid_xrds_discovery($xrds_url);
}
diff --git a/modules/openid/openid.test b/modules/openid/openid.test
index fc88158a0..7002c7a1f 100644
--- a/modules/openid/openid.test
+++ b/modules/openid/openid.test
@@ -59,6 +59,9 @@ class OpenIDFunctionalTest extends DrupalWebTestCase {
// element that contains the URL of an XRDS document.
$this->addIdentity(url('openid-test/yadis/http-equiv', array('absolute' => TRUE)), 2);
+ // Identifier is an XRI. Resolve using our own dummy proxy resolver.
+ variable_set('xri_proxy_resolver', url('openid-test/yadis/xrds/xri', array('absolute' => TRUE)) . '/');
+ $this->addIdentity('@example*résumé;%25', 2, 'http://example.com/user');
// HTML-based discovery:
// If the User-supplied Identifier is a URL of an HTML page, the page may
diff --git a/modules/openid/tests/openid_test.module b/modules/openid/tests/openid_test.module
index e980bb6c8..db03641c5 100644
--- a/modules/openid/tests/openid_test.module
+++ b/modules/openid/tests/openid_test.module
@@ -69,10 +69,29 @@ function openid_test_menu() {
*/
function openid_test_yadis_xrds() {
if ($_SERVER['HTTP_ACCEPT'] == 'application/xrds+xml') {
+ // Only respond to XRI requests for one specific XRI. The is used to verify
+ // that the XRI has been properly encoded. The "+" sign in the _xrd_r query
+ // parameter is decoded to a space by PHP.
+ if (arg(3) == 'xri') {
+ if (variable_get('clean_url', 0)) {
+ if (arg(4) != '@example*résumé;%25' || $_GET['_xrd_r'] != 'application/xrds xml') {
+ drupal_not_found();
+ }
+ }
+ else {
+ // Drupal cannot properly emulate an XRI proxy resolver using unclean
+ // URLs, so the arguments gets messed up.
+ if (arg(4) . '/' . arg(5) != '@example*résumé;%25?_xrd_r=application/xrds xml') {
+ drupal_not_found();
+ }
+ }
+ }
drupal_add_http_header('Content-Type', 'application/xrds+xml');
print '<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
<XRD>
+ <ProviderID>xri://@</ProviderID>
+ <CanonicalID>http://example.com/user</CanonicalID>
<Service>
<Type>http://example.com/this-is-ignored</Type>
</Service>
@@ -102,7 +121,7 @@ function openid_test_yadis_xrds() {
</Service>';
}
print '
- <XRD>
+ </XRD>
</xrds:XRDS>';
}
else {
diff --git a/modules/openid/xrds.inc b/modules/openid/xrds.inc
deleted file mode 100644
index 6e05d0c09..000000000
--- a/modules/openid/xrds.inc
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-// $Id$
-
-// Global variables to track parsing state
-$xrds_open_elements = array();
-$xrds_services = array();
-$xrds_current_service = array();
-
-/**
- * Main entry point for parsing XRDS documents
- */
-function xrds_parse($xml) {
- global $xrds_services;
-
- $parser = xml_parser_create_ns();
- xml_set_element_handler($parser, '_xrds_element_start', '_xrds_element_end');
- xml_set_character_data_handler($parser, '_xrds_cdata');
-
- xml_parse($parser, $xml);
- xml_parser_free($parser);
-
- return $xrds_services;
-}
-
-/**
- * Parser callback functions
- */
-function _xrds_element_start(&$parser, $name, $attributes) {
- global $xrds_open_elements, $xrds_current_service;
-
- $xrds_open_elements[] = _xrds_strip_namespace($name);
-
- $path = strtoupper(implode('/', $xrds_open_elements));
- if ($path == 'XRDS/XRD/SERVICE') {
- foreach ($attributes as $attribute_name => $value) {
- if (_xrds_strip_namespace($attribute_name) == 'PRIORITY') {
- $xrds_current_service['priority'] = intval($value);
- }
- }
- }
-}
-
-function _xrds_element_end(&$parser, $name) {
- global $xrds_open_elements, $xrds_services, $xrds_current_service;
-
- $name = _xrds_strip_namespace($name);
- if ($name == 'SERVICE') {
- if (!isset($xrds_current_service['priority'])) {
- // If the priority attribute is absent, the default is infinity.
- $xrds_current_service['priority'] = PHP_INT_MAX;
- }
- $xrds_services[] = $xrds_current_service;
- $xrds_current_service = array();
- }
- array_pop($xrds_open_elements);
-}
-
-function _xrds_cdata(&$parser, $data) {
- global $xrds_open_elements, $xrds_services, $xrds_current_service;
- $path = strtoupper(implode('/', $xrds_open_elements));
- switch ($path) {
- case 'XRDS/XRD/SERVICE/TYPE':
- $xrds_current_service['types'][] = $data;
- break;
- case 'XRDS/XRD/SERVICE/URI':
- $xrds_current_service['uri'] = $data;
- break;
- case 'XRDS/XRD/SERVICE/DELEGATE':
- $xrds_current_service['delegate'] = $data;
- break;
- case 'XRDS/XRD/SERVICE/LOCALID':
- $xrds_current_service['localid'] = $data;
- break;
- default:
- if (preg_match('@^XRDS/XRD/SERVICE/(.*)$@', $path, $matches)) {
- $xrds_current_service['additional'][$matches[1]] = $data;
- }
- break;
- }
-}
-
-function _xrds_strip_namespace($name) {
- // Strip namespacing.
- $pos = strrpos($name, ':');
- if ($pos !== FALSE) {
- $name = substr($name, $pos + 1, strlen($name));
- }
-
- return $name;
-}