/[drupal]/contributions/modules/openid_provider/openid_provider.inc
ViewVC logotype

Contents of /contributions/modules/openid_provider/openid_provider.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.3 - (show annotations) (download) (as text)
Sun Sep 7 03:41:22 2008 UTC (14 months, 2 weeks ago) by walkah
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.2: +23 -6 lines
File MIME type: text/x-php
Update math functions to use the faster GMP versions if available. See
http://php.net/gmp .
1 <?php
2 // $Id: openid_provider.inc,v 1.2 2008/05/26 18:20:30 walkah Exp $
3
4 /**
5 * Create an association with an RP
6 *
7 * @param array $request
8 */
9 function openid_provider_association_response($request) {
10 module_load_include('inc', 'openid');
11
12 $session_type = $request['openid.session_type'];
13 $assoc_type = $request['openid.assoc_type'];
14 $dh_modulus = $request['openid.dh_modulus'];
15 $dh_gen = $request['openid.dh_gen'];
16 $dh_consumer_public = $request['openid.dh_consumer_public'];
17
18 $assoc_handle = _openid_provider_nonce();
19 $expires_in = variable_get('openid_provider_assoc_expires_in', '3600');
20
21 // CLEAR STALE ASSOCIATIONS
22 db_query("DELETE FROM {openid_provider_association} WHERE created + expires_in < %d", time());
23
24 $response = array(
25 'ns' => OPENID_NS_2_0,
26 'session_type' => $session_type,
27 'assoc_handle' => $assoc_handle,
28 'assoc_type' => $assoc_type,
29 'expires_in' => $expires_in
30 );
31
32 if ($session_type == 'DH-SHA1'
33 || (($session_type == '' || $session_type == 'no-encryption')
34 && $assoc_type == 'HMAC-SHA1')) {
35 $num_bytes = 20;
36 $algo = 'sha1';
37 }
38 elseif ($session_type == 'DH-SHA256'
39 || (($session_type == '' || $session_type == 'no-encryption')
40 && $assoc_type == 'HMAC-SHA256')) {
41 $num_bytes = 32;
42 $algo = 'sha256';
43 }
44 $secret = _openid_get_bytes($num_bytes);
45 if ($session_type == '' || $session_type == 'no-encryption') {
46 $mac_key = hash_hmac($algo, $response['assoc_handle'], $secret, true);
47 $response['mac_key'] = $mac_key;
48 }
49 else {
50 $dh_assoc = openid_provider_dh_assoc($request, $secret, $algo);
51 $mac_key = base64_encode($secret);
52 $response['dh_server_public'] = $dh_assoc['dh_server_public'];
53 $response['enc_mac_key'] = $dh_assoc['enc_mac_key'];
54 }
55 // Save the association for reference when dealing
56 // with future requests from the same RP.
57 db_query("INSERT INTO {openid_provider_association} (assoc_handle, assoc_type, session_type, mac_key, created, expires_in) VALUES ('%s', '%s', '%s', '%s', %d, %d)",
58 $assoc_handle, $assoc_type, $session_type, $mac_key, time(), $expires_in);
59
60 $message = _openid_create_message($response);
61
62 drupal_set_header('HTTP/1.1 200 OK');
63 drupal_set_header("Content-Type: text/plain");
64 print $message;
65 }
66
67 /**
68 * Generate an association error response
69 */
70 function openid_provider_association_error() {
71 return array(
72 'error' => '', // optional
73 'error_code' => 'unsupported-type',
74 'session_type' => '', // optional
75 'assoc_type' => '' // optional
76 );
77 }
78
79 /**
80 * Generate an authentication response
81 *
82 * @param
83 */
84 function openid_provider_authentication_response($request) {
85 global $user;
86
87 // If the user is not yet logged in, redirect to the login page before continuing.
88 if (!$user->uid) {
89 $_SESSION['openid_provider']['request'] = $request;
90 drupal_goto('user/login', 'destination=openid/provider/continue');
91 }
92
93 // Determine the realm (openid.trust_root in 1.x)
94 $realm = (empty($request['openid.realm'])) ? $request['openid.trust_root'] : $request['openid.realm'];
95
96 // Check for a directed identity request.
97 if ($request['openid.identity'] == 'http://specs.openid.net/auth/2.0/identifier_select') {
98 $identity = url('user/' . $user->uid, array('absolute' => TRUE));
99 }
100 else {
101 $identity = $request['openid.identity'];
102 if ($identity != url('user/'. $user->uid, array('absolute' => TRUE))) {
103 $response = openid_provider_authentication_error($request['openid.mode']);
104 openid_redirect($request['openid.return_to'], $response);
105 }
106 }
107
108 $response = array(
109 'openid.ns' => OPENID_NS_2_0,
110 'openid.mode' => 'id_res',
111 'openid.op_endpoint' => url('openid/provider', array('absolute' => TRUE)),
112 'openid.identity' => $identity,
113 'openid.claimed_id' => $identity,
114 'openid.return_to' => $request['openid.return_to'],
115 'openid.response_nonce' => _openid_provider_nonce(),
116 'openid.assoc_handle' => $request['openid.assoc_handle']
117 );
118
119 // Is the RP requesting Immediate or Indirect mode?
120 if ($request['openid.mode'] == 'checkid_immediate') {
121 // TODO
122 }
123
124 $parts = parse_url($request['openid.return_to']);
125 if (isset($parts['query'])) {
126 $query = $parts['query'];
127 $q = _openid_get_params($query);
128 foreach ($q as $key => $val) {
129 $response[$key] = $val;
130 }
131 }
132
133 $rp = _openid_provider_rp_load($user->uid, $realm);
134 if ($rp->auto_release) {
135 $response = _openid_provider_sign($response);
136 _openid_provider_rp_save($user->uid, $realm, TRUE);
137 return openid_redirect_http($response['openid.return_to'], $response);
138 }
139 else {
140 return drupal_get_form('openid_provider_form', $response, $realm);
141 }
142 }
143
144 /**
145 * Negative assertion
146 */
147 function openid_provider_authentication_error($mode) {
148 if ($mode == 'checkid_immediate') {
149 return array(
150 'openid.mode' => 'id_res',
151 'openid.user_setup_url' => url('user/login', NULL, NULL, TRUE)
152 );
153 }
154 else { // checkid_setup
155 return array(
156 'openid.mode' => 'cancel'
157 );
158 }
159 }
160
161
162
163 function openid_provider_dh_assoc($request, $secret, $algo = 'sha1') {
164 if (empty($request['openid.dh_consumer_public'])) {
165 return FALSE;
166 }
167
168 if (isset($request['openid.dh_modulus'])) {
169 $mod = _openid_dh_base64_to_long($request['openid.dh_modulus']);
170 }
171 else {
172 $mod = OPENID_DH_DEFAULT_MOD;
173 }
174
175 if (isset($request['openid.dh_gen'])) {
176 $gen = _openid_dh_base64_to_long($request['openid.dh_gen']);
177 }
178 else {
179 $gen = OPENID_DH_DEFAULT_GEN;
180 }
181
182 $r = _openid_dh_rand($mod);
183 $private = _openid_provider_add($r, 1);
184 $public = _openid_provider_powmod($gen, $private, $mod);
185
186 $cpub = _openid_dh_base64_to_long($request['openid.dh_consumer_public']);
187 $shared = _openid_provider_powmod($cpub, $private, $mod);
188 $mac_key = _openid_provider_dh_xorsecret($shared, $secret, $algo);
189 $enc_mac_key = base64_encode($mac_key);
190 $spub64 = _openid_dh_long_to_base64($public);
191 return array(
192 'dh_server_public' => $spub64,
193 'enc_mac_key' => $enc_mac_key
194 );
195 }
196
197 /**
198 * Is copy of _opend_dh_xorsecret() but uses PHP5 hash() function. Should be merged back into openid client
199 * for D7.
200 *
201 * @param long $shared
202 * @param string $secret
203 * @param string $algo
204 * @return binary string
205 */
206 function _openid_provider_dh_xorsecret($shared, $secret, $algo = 'sha1') {
207 $dh_shared_str = _openid_dh_long_to_binary($shared);
208 $sha1_dh_shared = hash($algo, $dh_shared_str, true);
209 $xsecret = "";
210 for ($i = 0; $i < strlen($secret); $i++) {
211 $xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i]));
212 }
213 return $xsecret;
214 }
215
216 // 9.2.2.2. Verifying Directly with the Identity Provider
217 // 9.2.2.2.2. Response Parameters
218 // Request is: Exact copies of all fields from the authentication response
219 function openid_provider_verification_response($request) {
220 $is_valid = TRUE;
221
222 // Use the request openid.assoc_handle to look up
223 // how this message should be signed, based on
224 // a previously-created association.
225 $assoc = db_fetch_object(db_query("SELECT * FROM {openid_provider_association} WHERE assoc_handle = '%s'",
226 $request['openid.assoc_handle']));
227
228 $signed_keys = explode(',', $request['openid.signed']);
229 $signature = _openid_provider_signature($assoc, $request, $signed_keys);
230
231 if ($signature != $request['openid.sig']) {
232 $is_valid = FALSE;
233 }
234
235 if ($is_valid) {
236 $response = array('is_valid' => 'true');
237 }
238 else {
239 $response = array(
240 'is_valid' => 'false',
241 'invalidate_handle' => $request['openid.assoc_handle'] // optional, An association handle sent in the request
242 );
243 }
244
245 $message = _openid_create_message($response);
246 header("Content-Type: text/plain");
247 print $message;
248 }
249
250 function openid_provider_cancel_authentication_response($mode = 'checkid_immediate') {
251 $response = array();
252 if ($mode == 'checkid_immediate') {
253 $response = array(
254 'openid.ns' => OPENID_NS_2_0,
255 'openid.mode' => 'id_res',
256 'openid.user_setup_url' => url('user', NULL, NULL, TRUE)
257 );
258 }
259 else {
260 $response = array('openid.module' => OPENID_NS_2_0, 'openid.mode' => 'cancel');
261 }
262 return $response;
263 }
264
265 function _openid_provider_rp_load($uid, $realm = NULL) {
266 if ($realm) {
267 return db_fetch_object(db_query("SELECT * FROM {openid_provider_relying_party} WHERE uid=%d AND realm='%s'", $uid, $realm));
268 }
269 else {
270 $rps = array();
271 $result = db_query("SELECT * FROM {openid_provider_relying_party} WHERE uid=%d ORDER BY last_time DESC", $uid);
272 while ($rp = db_fetch_object($result)){
273 $rps[] = $rp;
274 }
275 return $rps;
276 }
277 }
278
279 function _openid_provider_rp_save($uid, $realm, $auto_release = FALSE) {
280 $rpid = db_result(db_query("SELECT rpid FROM {openid_provider_relying_party} WHERE uid=%d AND realm='%s'", $uid, $realm));
281 if ($rpid) {
282 db_query("UPDATE {openid_provider_relying_party} SET auto_release=%d, last_time=%d WHERE rpid=%d", $auto_release, time(), $rpid);
283 }
284 else {
285 db_query("INSERT INTO {openid_provider_relying_party} (uid, realm, first_time, last_time, auto_release) VALUES (%d, '%s', %d, %d, %d)", $uid, $realm, time(), time(), $auto_release);
286 }
287 }
288 function _openid_provider_nonce() {
289 // YYYY-MM-DDThh:mm:ssTZD UTC, plus some optional extra unique chars
290 return gmstrftime('%Y-%m-%dT%H:%M:%SZ') .
291 chr(mt_rand(0, 25) + 65) .
292 chr(mt_rand(0, 25) + 65) .
293 chr(mt_rand(0, 25) + 65) .
294 chr(mt_rand(0, 25) + 65);
295 }
296
297 function _openid_provider_sign($response) {
298 module_load_include('inc', 'openid');
299
300 $also_sign = array();
301 $parts = parse_url($response['openid.return_to']);
302 if (isset($parts['query'])) {
303 $query = $parts['query'];
304 $q = _openid_get_params($query);
305 foreach ($q as $key => $val) {
306 $also_sign[] = $key;
307 $response[$key] = $val;
308 }
309 }
310
311 $signed_keys = array('op_endpoint', 'return_to', 'response_nonce', 'assoc_handle', 'identity', 'claimed_id');
312 $signed_keys = array_merge($signed_keys, module_invoke_all('openid', 'signed', $response));
313 $response['openid.signed'] = implode(',', $signed_keys);
314
315 // Use the request openid.assoc_handle to look up
316 // how this message should be signed, based on
317 // a previously-created association.
318 $assoc = db_fetch_object(db_query("SELECT * FROM {openid_provider_association} WHERE assoc_handle = '%s'",
319 $response['openid.assoc_handle']));
320
321 // Generate signature for this message
322 $response['openid.sig'] = _openid_provider_signature($assoc, $response, $signed_keys);
323 return $response;
324 }
325
326 /**
327 * Is copy from openid client but uses PHP5 only hash_hmac() function.
328 *
329 * @param object $association
330 * @param array $message_array
331 * @param array $keys_to_sign
332 * @return string
333 */
334 function _openid_provider_signature($association, $message_array, $keys_to_sign) {
335 $signature = '';
336 $sign_data = array();
337 foreach ($keys_to_sign as $key) {
338 if (isset($message_array['openid.'. $key])) {
339 $sign_data[$key] = $message_array['openid.'. $key];
340 }
341 }
342 $message = _openid_create_message($sign_data);
343 $secret = base64_decode($association->mac_key);
344 $signature = hash_hmac($association->assoc_type == 'HMAC-SHA256' ? 'sha256' : 'sha1', $message, $secret, true);
345 return base64_encode($signature);
346 }
347
348 function _openid_provider_add($a, $b) {
349 if (function_exists('gmp_add')) {
350 return gmp_add($a, $b);
351 }
352 else if (function_exists('bcadd')) {
353 bcadd($a, $b);
354 }
355 }
356
357 function _openid_provider_powmod($base, $exp, $mod) {
358 if (function_exists('gmp_powm')) {
359 return gmp_powm($base, $exp, $mod);
360 }
361 else if (function_exists('bcpowmod')) {
362 return bcpowmod($base, $exp, $mod);
363 }
364 }

  ViewVC Help
Powered by ViewVC 1.1.2