Mercurial > defr > drupal > core
comparison modules/openid/openid.inc @ 1:c1f4ac30525a 6.0
Drupal 6.0
author | Franck Deroche <webmaster@defr.org> |
---|---|
date | Tue, 23 Dec 2008 14:28:28 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:5a113a1c4740 | 1:c1f4ac30525a |
---|---|
1 <?php | |
2 // $Id: openid.inc,v 1.8 2008/01/30 22:11:22 goba Exp $ | |
3 | |
4 /** | |
5 * @file | |
6 * OpenID utility functions. | |
7 */ | |
8 | |
9 // Diffie-Hellman Key Exchange Default Value. | |
10 define('OPENID_DH_DEFAULT_MOD', '155172898181473697471232257763715539915724801'. | |
11 '966915404479707795314057629378541917580651227423698188993727816152646631'. | |
12 '438561595825688188889951272158842675419950341258706556549803580104870537'. | |
13 '681476726513255747040765857479291291572334510643245094715007229621094194'. | |
14 '349783925984760375594985848253359305585439638443'); | |
15 | |
16 // Constants for Diffie-Hellman key exchange computations. | |
17 define('OPENID_DH_DEFAULT_GEN', '2'); | |
18 define('OPENID_SHA1_BLOCKSIZE', 64); | |
19 define('OPENID_RAND_SOURCE', '/dev/urandom'); | |
20 | |
21 // OpenID namespace URLs | |
22 define('OPENID_NS_2_0', 'http://specs.openid.net/auth/2.0'); | |
23 define('OPENID_NS_1_1', 'http://openid.net/signon/1.1'); | |
24 define('OPENID_NS_1_0', 'http://openid.net/signon/1.0'); | |
25 | |
26 /** | |
27 * Performs an HTTP 302 redirect (for the 1.x protocol). | |
28 */ | |
29 function openid_redirect_http($url, $message) { | |
30 $query = array(); | |
31 foreach ($message as $key => $val) { | |
32 $query[] = $key .'='. urlencode($val); | |
33 } | |
34 | |
35 $sep = (strpos($url, '?') === FALSE) ? '?' : '&'; | |
36 header('Location: '. $url . $sep . implode('&', $query), TRUE, 302); | |
37 exit; | |
38 } | |
39 | |
40 /** | |
41 * Creates a js auto-submit redirect for (for the 2.x protocol) | |
42 */ | |
43 function openid_redirect($url, $message) { | |
44 $output = '<html><head><title>'. t('OpenID redirect') ."</title></head>\n<body>"; | |
45 $output .= drupal_get_form('openid_redirect_form', $url, $message); | |
46 $output .= '<script type="text/javascript">document.getElementById("openid-redirect-form").submit();</script>'; | |
47 $output .= "</body></html>\n"; | |
48 print $output; | |
49 exit; | |
50 } | |
51 | |
52 function openid_redirect_form(&$form_state, $url, $message) { | |
53 $form = array(); | |
54 $form['#action'] = $url; | |
55 $form['#method'] = "post"; | |
56 foreach ($message as $key => $value) { | |
57 $form[$key] = array( | |
58 '#type' => 'hidden', | |
59 '#name' => $key, | |
60 '#value' => $value, | |
61 ); | |
62 } | |
63 $form['submit'] = array( | |
64 '#type' => 'submit', | |
65 '#prefix' => '<noscript>', | |
66 '#suffix' => '</noscript>', | |
67 '#value' => t('Send'), | |
68 ); | |
69 | |
70 return $form; | |
71 } | |
72 | |
73 /** | |
74 * Determine if the given identifier is an XRI ID. | |
75 */ | |
76 function _openid_is_xri($identifier) { | |
77 $firstchar = substr($identifier, 0, 1); | |
78 if ($firstchar == "@" || $firstchar == "=") | |
79 return TRUE; | |
80 | |
81 if (stristr($identifier, 'xri://') !== FALSE) { | |
82 return TRUE; | |
83 } | |
84 | |
85 return FALSE; | |
86 } | |
87 | |
88 /** | |
89 * Normalize the given identifier as per spec. | |
90 */ | |
91 function _openid_normalize($identifier) { | |
92 if (_openid_is_xri($identifier)) { | |
93 return _openid_normalize_xri($identifier); | |
94 } | |
95 else { | |
96 return _openid_normalize_url($identifier); | |
97 } | |
98 } | |
99 | |
100 function _openid_normalize_xri($xri) { | |
101 $normalized_xri = $xri; | |
102 if (stristr($xri, 'xri://') !== FALSE) { | |
103 $normalized_xri = substr($xri, 6); | |
104 } | |
105 return $normalized_xri; | |
106 } | |
107 | |
108 function _openid_normalize_url($url) { | |
109 $normalized_url = $url; | |
110 | |
111 if (stristr($url, '://') === FALSE) { | |
112 $normalized_url = 'http://'. $url; | |
113 } | |
114 | |
115 if (substr_count($normalized_url, '/') < 3) { | |
116 $normalized_url .= '/'; | |
117 } | |
118 | |
119 return $normalized_url; | |
120 } | |
121 | |
122 /** | |
123 * Create a serialized message packet as per spec: $key:$value\n . | |
124 */ | |
125 function _openid_create_message($data) { | |
126 $serialized = ''; | |
127 | |
128 foreach ($data as $key => $value) { | |
129 if ((strpos($key, ':') !== FALSE) || (strpos($key, "\n") !== FALSE) || (strpos($value, "\n") !== FALSE)) { | |
130 return null; | |
131 } | |
132 $serialized .= "$key:$value\n"; | |
133 } | |
134 return $serialized; | |
135 } | |
136 | |
137 /** | |
138 * Encode a message from _openid_create_message for HTTP Post | |
139 */ | |
140 function _openid_encode_message($message) { | |
141 $encoded_message = ''; | |
142 | |
143 $items = explode("\n", $message); | |
144 foreach ($items as $item) { | |
145 $parts = explode(':', $item, 2); | |
146 | |
147 if (count($parts) == 2) { | |
148 if ($encoded_message != '') { | |
149 $encoded_message .= '&'; | |
150 } | |
151 $encoded_message .= rawurlencode(trim($parts[0])) .'='. rawurlencode(trim($parts[1])); | |
152 } | |
153 } | |
154 | |
155 return $encoded_message; | |
156 } | |
157 | |
158 /** | |
159 * Convert a direct communication message | |
160 * into an associative array. | |
161 */ | |
162 function _openid_parse_message($message) { | |
163 $parsed_message = array(); | |
164 | |
165 $items = explode("\n", $message); | |
166 foreach ($items as $item) { | |
167 $parts = explode(':', $item, 2); | |
168 | |
169 if (count($parts) == 2) { | |
170 $parsed_message[$parts[0]] = $parts[1]; | |
171 } | |
172 } | |
173 | |
174 return $parsed_message; | |
175 } | |
176 | |
177 /** | |
178 * Return a nonce value - formatted per OpenID spec. | |
179 */ | |
180 function _openid_nonce() { | |
181 // YYYY-MM-DDThh:mm:ssTZD UTC, plus some optional extra unique chars | |
182 return gmstrftime('%Y-%m-%dT%H:%M:%S%Z') . | |
183 chr(mt_rand(0, 25) + 65) . | |
184 chr(mt_rand(0, 25) + 65) . | |
185 chr(mt_rand(0, 25) + 65) . | |
186 chr(mt_rand(0, 25) + 65); | |
187 } | |
188 | |
189 /** | |
190 * Pull the href attribute out of an html link element. | |
191 */ | |
192 function _openid_link_href($rel, $html) { | |
193 $rel = preg_quote($rel); | |
194 preg_match('|<link\s+rel=["\'](.*)'. $rel .'(.*)["\'](.*)/?>|iUs', $html, $matches); | |
195 if (isset($matches[3])) { | |
196 preg_match('|href=["\']([^"]+)["\']|iU', $matches[3], $href); | |
197 return trim($href[1]); | |
198 } | |
199 return FALSE; | |
200 } | |
201 | |
202 /** | |
203 * Pull the http-equiv attribute out of an html meta element | |
204 */ | |
205 function _openid_meta_httpequiv($equiv, $html) { | |
206 preg_match('|<meta\s+http-equiv=["\']'. $equiv .'["\'](.*)/?>|iUs', $html, $matches); | |
207 if (isset($matches[1])) { | |
208 preg_match('|content=["\']([^"]+)["\']|iUs', $matches[1], $content); | |
209 if (isset($content[1])) { | |
210 return $content[1]; | |
211 } | |
212 } | |
213 return FALSE; | |
214 } | |
215 | |
216 /** | |
217 * Sign certain keys in a message | |
218 * @param $association - object loaded from openid_association or openid_server_association table | |
219 * - important fields are ->assoc_type and ->mac_key | |
220 * @param $message_array - array of entire message about to be sent | |
221 * @param $keys_to_sign - keys in the message to include in signature (without | |
222 * 'openid.' appended) | |
223 */ | |
224 function _openid_signature($association, $message_array, $keys_to_sign) { | |
225 $signature = ''; | |
226 $sign_data = array(); | |
227 | |
228 foreach ($keys_to_sign as $key) { | |
229 if (isset($message_array['openid.'. $key])) { | |
230 $sign_data[$key] = $message_array['openid.'. $key]; | |
231 } | |
232 } | |
233 | |
234 $message = _openid_create_message($sign_data); | |
235 $secret = base64_decode($association->mac_key); | |
236 $signature = _openid_hmac($secret, $message); | |
237 | |
238 return base64_encode($signature); | |
239 } | |
240 | |
241 function _openid_hmac($key, $text) { | |
242 if (strlen($key) > OPENID_SHA1_BLOCKSIZE) { | |
243 $key = _openid_sha1($key, true); | |
244 } | |
245 | |
246 $key = str_pad($key, OPENID_SHA1_BLOCKSIZE, chr(0x00)); | |
247 $ipad = str_repeat(chr(0x36), OPENID_SHA1_BLOCKSIZE); | |
248 $opad = str_repeat(chr(0x5c), OPENID_SHA1_BLOCKSIZE); | |
249 $hash1 = _openid_sha1(($key ^ $ipad) . $text, true); | |
250 $hmac = _openid_sha1(($key ^ $opad) . $hash1, true); | |
251 | |
252 return $hmac; | |
253 } | |
254 | |
255 function _openid_sha1($text) { | |
256 $hex = sha1($text); | |
257 $raw = ''; | |
258 for ($i = 0; $i < 40; $i += 2) { | |
259 $hexcode = substr($hex, $i, 2); | |
260 $charcode = (int)base_convert($hexcode, 16, 10); | |
261 $raw .= chr($charcode); | |
262 } | |
263 return $raw; | |
264 } | |
265 | |
266 function _openid_dh_base64_to_long($str) { | |
267 $b64 = base64_decode($str); | |
268 | |
269 return _openid_dh_binary_to_long($b64); | |
270 } | |
271 | |
272 function _openid_dh_long_to_base64($str) { | |
273 return base64_encode(_openid_dh_long_to_binary($str)); | |
274 } | |
275 | |
276 function _openid_dh_binary_to_long($str) { | |
277 $bytes = array_merge(unpack('C*', $str)); | |
278 | |
279 $n = 0; | |
280 foreach ($bytes as $byte) { | |
281 $n = bcmul($n, pow(2, 8)); | |
282 $n = bcadd($n, $byte); | |
283 } | |
284 | |
285 return $n; | |
286 } | |
287 | |
288 function _openid_dh_long_to_binary($long) { | |
289 $cmp = bccomp($long, 0); | |
290 if ($cmp < 0) { | |
291 return FALSE; | |
292 } | |
293 | |
294 if ($cmp == 0) { | |
295 return "\x00"; | |
296 } | |
297 | |
298 $bytes = array(); | |
299 | |
300 while (bccomp($long, 0) > 0) { | |
301 array_unshift($bytes, bcmod($long, 256)); | |
302 $long = bcdiv($long, pow(2, 8)); | |
303 } | |
304 | |
305 if ($bytes && ($bytes[0] > 127)) { | |
306 array_unshift($bytes, 0); | |
307 } | |
308 | |
309 $string = ''; | |
310 foreach ($bytes as $byte) { | |
311 $string .= pack('C', $byte); | |
312 } | |
313 | |
314 return $string; | |
315 } | |
316 | |
317 function _openid_dh_xorsecret($shared, $secret) { | |
318 $dh_shared_str = _openid_dh_long_to_binary($shared); | |
319 $sha1_dh_shared = _openid_sha1($dh_shared_str); | |
320 $xsecret = ""; | |
321 for ($i = 0; $i < strlen($secret); $i++) { | |
322 $xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i])); | |
323 } | |
324 | |
325 return $xsecret; | |
326 } | |
327 | |
328 function _openid_dh_rand($stop) { | |
329 static $duplicate_cache = array(); | |
330 | |
331 // Used as the key for the duplicate cache | |
332 $rbytes = _openid_dh_long_to_binary($stop); | |
333 | |
334 if (array_key_exists($rbytes, $duplicate_cache)) { | |
335 list($duplicate, $nbytes) = $duplicate_cache[$rbytes]; | |
336 } | |
337 else { | |
338 if ($rbytes[0] == "\x00") { | |
339 $nbytes = strlen($rbytes) - 1; | |
340 } | |
341 else { | |
342 $nbytes = strlen($rbytes); | |
343 } | |
344 | |
345 $mxrand = bcpow(256, $nbytes); | |
346 | |
347 // If we get a number less than this, then it is in the | |
348 // duplicated range. | |
349 $duplicate = bcmod($mxrand, $stop); | |
350 | |
351 if (count($duplicate_cache) > 10) { | |
352 $duplicate_cache = array(); | |
353 } | |
354 | |
355 $duplicate_cache[$rbytes] = array($duplicate, $nbytes); | |
356 } | |
357 | |
358 do { | |
359 $bytes = "\x00". _openid_get_bytes($nbytes); | |
360 $n = _openid_dh_binary_to_long($bytes); | |
361 // Keep looping if this value is in the low duplicated range. | |
362 } while (bccomp($n, $duplicate) < 0); | |
363 | |
364 return bcmod($n, $stop); | |
365 } | |
366 | |
367 function _openid_get_bytes($num_bytes) { | |
368 static $f = null; | |
369 $bytes = ''; | |
370 if (!isset($f)) { | |
371 $f = @fopen(OPENID_RAND_SOURCE, "r"); | |
372 } | |
373 if (!$f) { | |
374 // pseudorandom used | |
375 $bytes = ''; | |
376 for ($i = 0; $i < $num_bytes; $i += 4) { | |
377 $bytes .= pack('L', mt_rand()); | |
378 } | |
379 $bytes = substr($bytes, 0, $num_bytes); | |
380 } | |
381 else { | |
382 $bytes = fread($f, $num_bytes); | |
383 } | |
384 return $bytes; | |
385 } | |
386 | |
387 function _openid_response($str = NULL) { | |
388 $data = array(); | |
389 | |
390 if (isset($_SERVER['REQUEST_METHOD'])) { | |
391 $data = _openid_get_params($_SERVER['QUERY_STRING']); | |
392 | |
393 if ($_SERVER['REQUEST_METHOD'] == 'POST') { | |
394 $str = file_get_contents('php://input'); | |
395 | |
396 $post = array(); | |
397 if ($str !== false) { | |
398 $post = _openid_get_params($str); | |
399 } | |
400 | |
401 $data = array_merge($data, $post); | |
402 } | |
403 } | |
404 | |
405 return $data; | |
406 } | |
407 | |
408 function _openid_get_params($str) { | |
409 $chunks = explode("&", $str); | |
410 | |
411 $data = array(); | |
412 foreach ($chunks as $chunk) { | |
413 $parts = explode("=", $chunk, 2); | |
414 | |
415 if (count($parts) == 2) { | |
416 list($k, $v) = $parts; | |
417 $data[$k] = urldecode($v); | |
418 } | |
419 } | |
420 return $data; | |
421 } | |
422 | |
423 /** | |
424 * Provide bcpowmod support for PHP4. | |
425 */ | |
426 if (!function_exists('bcpowmod')) { | |
427 function bcpowmod($base, $exp, $mod) { | |
428 $square = bcmod($base, $mod); | |
429 $result = 1; | |
430 while (bccomp($exp, 0) > 0) { | |
431 if (bcmod($exp, 2)) { | |
432 $result = bcmod(bcmul($result, $square), $mod); | |
433 } | |
434 $square = bcmod(bcmul($square, $square), $mod); | |
435 $exp = bcdiv($exp, 2); | |
436 } | |
437 return $result; | |
438 } | |
439 } |