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