|
1 <?php |
|
2 |
|
3 /* |
|
4 * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between |
|
5 * Version 1.0 (Banshee) |
|
6 * Copyright (C) 2006-2007 Dan Fuhry |
|
7 * sessions.php - everything related to security and user management |
|
8 * |
|
9 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
10 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
14 */ |
|
15 |
|
16 // Prepare a string for insertion into a MySQL database |
|
17 function filter($str) { return $db->escape($str); } |
|
18 |
|
19 /** |
|
20 * Anything and everything related to security and user management. This includes AES encryption, which is illegal in some countries. |
|
21 * Documenting the API was not easy - I hope you folks enjoy it. |
|
22 * @package Enano |
|
23 * @subpackage Session manager |
|
24 * @category security, user management, logins, etc. |
|
25 */ |
|
26 |
|
27 class sessionManager { |
|
28 |
|
29 # Variables |
|
30 |
|
31 /** |
|
32 * Whether we're logged in or not |
|
33 * @var bool |
|
34 */ |
|
35 |
|
36 var $user_logged_in = false; |
|
37 |
|
38 /** |
|
39 * Our current low-privilege session key |
|
40 * @var string |
|
41 */ |
|
42 |
|
43 var $sid; |
|
44 |
|
45 /** |
|
46 * Username of currently logged-in user, or IP address if not logged in |
|
47 * @var string |
|
48 */ |
|
49 |
|
50 var $username; |
|
51 |
|
52 /** |
|
53 * User ID of currently logged-in user, or -1 if not logged in |
|
54 * @var int |
|
55 */ |
|
56 |
|
57 var $user_id; |
|
58 |
|
59 /** |
|
60 * Real name of currently logged-in user, or blank if not logged in |
|
61 * @var string |
|
62 */ |
|
63 |
|
64 var $real_name; |
|
65 |
|
66 /** |
|
67 * E-mail address of currently logged-in user, or blank if not logged in |
|
68 * @var string |
|
69 */ |
|
70 |
|
71 var $email; |
|
72 |
|
73 /** |
|
74 * User level of current user |
|
75 * USER_LEVEL_GUEST: guest |
|
76 * USER_LEVEL_MEMBER: regular user |
|
77 * USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication) |
|
78 * USER_LEVEL_MOD: moderator |
|
79 * USER_LEVEL_ADMIN: administrator |
|
80 * @var int |
|
81 */ |
|
82 |
|
83 var $user_level; |
|
84 |
|
85 /** |
|
86 * High-privilege session key |
|
87 * @var string or false if not running on high-level authentication |
|
88 */ |
|
89 |
|
90 var $sid_super; |
|
91 |
|
92 /** |
|
93 * The user's theme preference, defaults to $template->default_theme |
|
94 * @var string |
|
95 */ |
|
96 |
|
97 var $theme; |
|
98 |
|
99 /** |
|
100 * The user's style preference, or style auto-detected based on theme if not logged in |
|
101 * @var string |
|
102 */ |
|
103 |
|
104 var $style; |
|
105 |
|
106 /** |
|
107 * Signature of current user - appended to comments, etc. |
|
108 * @var string |
|
109 */ |
|
110 |
|
111 var $signature; |
|
112 |
|
113 /** |
|
114 * UNIX timestamp of when we were registered, or 0 if not logged in |
|
115 * @var int |
|
116 */ |
|
117 |
|
118 var $reg_time; |
|
119 |
|
120 /** |
|
121 * MD5 hash of the current user's password, if applicable |
|
122 * @var string OR bool false |
|
123 */ |
|
124 |
|
125 var $password_hash; |
|
126 |
|
127 /** |
|
128 * The number of unread private messages this user has. |
|
129 * @var int |
|
130 */ |
|
131 |
|
132 var $unread_pms = 0; |
|
133 |
|
134 /** |
|
135 * AES key used to encrypt passwords and session key info - irreversibly destroyed when disallow_password_grab() is called |
|
136 * @var string |
|
137 */ |
|
138 |
|
139 var $private_key; |
|
140 |
|
141 /** |
|
142 * Regex that defines a valid username, minus the ^ and $, these are added later |
|
143 * @var string |
|
144 */ |
|
145 |
|
146 var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)'; |
|
147 |
|
148 /** |
|
149 * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param. |
|
150 * @var string |
|
151 */ |
|
152 |
|
153 var $auth_level = -1; |
|
154 |
|
155 /** |
|
156 * State variable to track if a session timed out |
|
157 * @var bool |
|
158 */ |
|
159 |
|
160 var $sw_timed_out = false; |
|
161 |
|
162 /** |
|
163 * Switch to track if we're started or not. |
|
164 * @access private |
|
165 * @var bool |
|
166 */ |
|
167 |
|
168 var $started = false; |
|
169 |
|
170 /** |
|
171 * Switch to control compatibility mode (for older Enano websites being upgraded) |
|
172 * @access private |
|
173 * @var bool |
|
174 */ |
|
175 |
|
176 var $compat = false; |
|
177 |
|
178 /** |
|
179 * Our list of permission types. |
|
180 * @access private |
|
181 * @var array |
|
182 */ |
|
183 |
|
184 var $acl_types = Array(); |
|
185 |
|
186 /** |
|
187 * The list of descriptions for the permission types |
|
188 * @var array |
|
189 */ |
|
190 |
|
191 var $acl_descs = Array(); |
|
192 |
|
193 /** |
|
194 * A list of dependencies for ACL types. |
|
195 * @var array |
|
196 */ |
|
197 |
|
198 var $acl_deps = Array(); |
|
199 |
|
200 /** |
|
201 * Our tell-all list of permissions. |
|
202 * @access private - or, preferably, protected |
|
203 * @var array |
|
204 */ |
|
205 |
|
206 var $perms = Array(); |
|
207 |
|
208 /** |
|
209 * A cache variable - saved after sitewide permissions are checked but before page-specific permissions. |
|
210 * @var array |
|
211 * @access private |
|
212 */ |
|
213 |
|
214 var $acl_base_cache = Array(); |
|
215 |
|
216 /** |
|
217 * Stores the scope information for ACL types. |
|
218 * @var array |
|
219 * @access private |
|
220 */ |
|
221 |
|
222 var $acl_scope = Array(); |
|
223 |
|
224 /** |
|
225 * Array to track which default permissions are being used |
|
226 * @var array |
|
227 * @access private |
|
228 */ |
|
229 |
|
230 var $acl_defaults_used = Array(); |
|
231 |
|
232 /** |
|
233 * Array to track group membership. |
|
234 * @var array |
|
235 */ |
|
236 |
|
237 var $groups = Array(); |
|
238 |
|
239 /** |
|
240 * Associative array to track group modship. |
|
241 * @var array |
|
242 */ |
|
243 |
|
244 var $group_mod = Array(); |
|
245 |
|
246 # Basic functions |
|
247 |
|
248 /** |
|
249 * Constructor. |
|
250 */ |
|
251 |
|
252 function __construct() |
|
253 { |
|
254 global $db, $session, $paths, $template, $plugins; // Common objects |
|
255 include(ENANO_ROOT.'/config.php'); |
|
256 unset($dbhost, $dbname, $dbuser, $dbpasswd); |
|
257 if(isset($crypto_key)) |
|
258 { |
|
259 $this->private_key = $crypto_key; |
|
260 $this->private_key = hexdecode($this->private_key); |
|
261 } |
|
262 else |
|
263 { |
|
264 if(is_writable(ENANO_ROOT.'/config.php')) |
|
265 { |
|
266 // Generate and stash a private key |
|
267 // This should only happen during an automated silent gradual migration to the new encryption platform. |
|
268 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
269 $this->private_key = $aes->gen_readymade_key(); |
|
270 |
|
271 $config = file_get_contents(ENANO_ROOT.'/config.php'); |
|
272 if(!$config) |
|
273 { |
|
274 die('$session->__construct(): can\'t get the contents of config.php'); |
|
275 } |
|
276 |
|
277 $config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config); |
|
278 // And while we're at it... |
|
279 $config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config); |
|
280 $fh = @fopen(ENANO_ROOT.'/config.php', 'w'); |
|
281 if ( !$fh ) |
|
282 { |
|
283 die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...'); |
|
284 } |
|
285 |
|
286 fwrite($fh, $config); |
|
287 fclose($fh); |
|
288 } |
|
289 else |
|
290 { |
|
291 die_semicritical('Crypto error', '<p>No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.</p>'); |
|
292 } |
|
293 } |
|
294 // Check for compatibility mode |
|
295 if(defined('IN_ENANO_INSTALL')) |
|
296 { |
|
297 $q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;'); |
|
298 if(!$q) |
|
299 { |
|
300 $error = mysql_error(); |
|
301 if(strstr($error, "Unknown column 'old_encryption'")) |
|
302 $this->compat = true; |
|
303 else |
|
304 $db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)'); |
|
305 } |
|
306 $db->free_result(); |
|
307 } |
|
308 } |
|
309 |
|
310 /** |
|
311 * PHP 4 compatible constructor. |
|
312 */ |
|
313 |
|
314 function sessionManager() |
|
315 { |
|
316 $this->__construct(); |
|
317 } |
|
318 |
|
319 /** |
|
320 * Wrapper function to sanitize strings for MySQL and HTML |
|
321 * @param string $text The text to sanitize |
|
322 * @return string |
|
323 */ |
|
324 |
|
325 function prepare_text($text) |
|
326 { |
|
327 global $db; |
|
328 return $db->escape(htmlspecialchars($text)); |
|
329 } |
|
330 |
|
331 /** |
|
332 * Makes a SQL query and handles error checking |
|
333 * @param string $query The SQL query to make |
|
334 * @return resource |
|
335 */ |
|
336 |
|
337 function sql($query) |
|
338 { |
|
339 global $db, $session, $paths, $template, $plugins; // Common objects |
|
340 $result = $db->sql_query($query); |
|
341 if(!$result) |
|
342 { |
|
343 $db->_die('The error seems to have occurred somewhere in the session management code.'); |
|
344 } |
|
345 return $result; |
|
346 } |
|
347 |
|
348 # Session restoration and permissions |
|
349 |
|
350 /** |
|
351 * Initializes the basic state of things, including most user prefs, login data, cookie stuff |
|
352 */ |
|
353 |
|
354 function start() |
|
355 { |
|
356 global $db, $session, $paths, $template, $plugins; // Common objects |
|
357 if($this->started) return; |
|
358 $this->started = true; |
|
359 $user = false; |
|
360 if(isset($_COOKIE['sid'])) |
|
361 { |
|
362 if($this->compat) |
|
363 { |
|
364 $userdata = $this->compat_validate_session($_COOKIE['sid']); |
|
365 } |
|
366 else |
|
367 { |
|
368 $userdata = $this->validate_session($_COOKIE['sid']); |
|
369 } |
|
370 if(is_array($userdata)) |
|
371 { |
|
372 $data = RenderMan::strToPageID($paths->get_pageid_from_url()); |
|
373 |
|
374 if(!$this->compat && $userdata['account_active'] != 1 && $data[1] != 'Special' && $data[1] != 'Admin') |
|
375 { |
|
376 $this->logout(); |
|
377 $a = getConfig('account_activation'); |
|
378 switch($a) |
|
379 { |
|
380 case 'none': |
|
381 default: |
|
382 $solution = 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.'; |
|
383 break; |
|
384 case 'user': |
|
385 $solution = 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.'; |
|
386 break; |
|
387 case 'admin': |
|
388 $solution = 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time the one of the administrators visits the site.'; |
|
389 break; |
|
390 } |
|
391 die_semicritical('Account error', '<p>It appears that your user account has not yet been activated. '.$solution.'</p>'); |
|
392 } |
|
393 |
|
394 $this->sid = $_COOKIE['sid']; |
|
395 $this->user_logged_in = true; |
|
396 $this->user_id = intval($userdata['user_id']); |
|
397 $this->username = $userdata['username']; |
|
398 $this->password_hash = $userdata['password']; |
|
399 $this->user_level = intval($userdata['user_level']); |
|
400 $this->real_name = $userdata['real_name']; |
|
401 $this->email = $userdata['email']; |
|
402 $this->unread_pms = $userdata['num_pms']; |
|
403 if(!$this->compat) |
|
404 { |
|
405 $this->theme = $userdata['theme']; |
|
406 $this->style = $userdata['style']; |
|
407 $this->signature = $userdata['signature']; |
|
408 $this->reg_time = $userdata['reg_time']; |
|
409 } |
|
410 // Small security risk here - it allows someone who has already authenticated as an administrator to store the "super" key in |
|
411 // the cookie. Change this to USER_LEVEL_MEMBER to override that. The same 15-minute restriction applies to this "exploit". |
|
412 $this->auth_level = $userdata['auth_level']; |
|
413 if(!isset($template->named_theme_list[$this->theme])) |
|
414 { |
|
415 if($this->compat || !is_object($template)) |
|
416 { |
|
417 $this->theme = 'oxygen'; |
|
418 $this->style = 'bleu'; |
|
419 } |
|
420 else |
|
421 { |
|
422 $this->theme = $template->default_theme; |
|
423 $this->style = $template->default_style; |
|
424 } |
|
425 } |
|
426 $user = true; |
|
427 |
|
428 if(isset($_REQUEST['auth']) && !$this->sid_super) |
|
429 { |
|
430 // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth. |
|
431 if($this->compat) |
|
432 { |
|
433 $key = $_REQUEST['auth']; |
|
434 $super = $this->compat_validate_session($key); |
|
435 } |
|
436 else |
|
437 { |
|
438 $key = strrev($_REQUEST['auth']); |
|
439 $super = $this->validate_session($key); |
|
440 } |
|
441 if(is_array($super)) |
|
442 { |
|
443 $this->auth_level = intval($super['auth_level']); |
|
444 $this->sid_super = $_REQUEST['auth']; |
|
445 } |
|
446 } |
|
447 } |
|
448 } |
|
449 if(!$user) |
|
450 { |
|
451 //exit; |
|
452 $this->register_guest_session(); |
|
453 } |
|
454 if(!$this->compat) |
|
455 { |
|
456 // init groups |
|
457 $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g |
|
458 LEFT JOIN '.table_prefix.'group_members AS m |
|
459 ON g.group_id=m.group_id |
|
460 WHERE ( m.user_id='.$this->user_id.' |
|
461 OR g.group_name=\'Everyone\') |
|
462 ' . ( enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . ' |
|
463 ORDER BY group_id ASC;'); // Make sure "Everyone" comes first so the permissions can be overridden |
|
464 if($row = $db->fetchrow()) |
|
465 { |
|
466 do { |
|
467 $this->groups[$row['group_id']] = $row['group_name']; |
|
468 $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 ); |
|
469 } while($row = $db->fetchrow()); |
|
470 } |
|
471 else |
|
472 { |
|
473 die('No group info'); |
|
474 } |
|
475 } |
|
476 $this->check_banlist(); |
|
477 |
|
478 if ( isset ( $_GET['printable'] ) ) |
|
479 { |
|
480 $this->theme = 'printable'; |
|
481 $this->style = 'default'; |
|
482 } |
|
483 |
|
484 } |
|
485 |
|
486 # Logins |
|
487 |
|
488 /** |
|
489 * Attempts to perform a login using crypto functions |
|
490 * @param string $username The username |
|
491 * @param string $aes_data The encrypted password, hex-encoded |
|
492 * @param string $aes_key The MD5 hash of the encryption key, hex-encoded |
|
493 * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt |
|
494 * @param int $level The privilege level we're authenticating for, defaults to 0 |
|
495 * @return string 'success' on success, or error string on failure |
|
496 */ |
|
497 |
|
498 function login_with_crypto($username, $aes_data, $aes_key, $challenge, $level = USER_LEVEL_MEMBER) |
|
499 { |
|
500 global $db, $session, $paths, $template, $plugins; // Common objects |
|
501 |
|
502 $privcache = $this->private_key; |
|
503 |
|
504 // Instanciate the Rijndael encryption object |
|
505 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
506 |
|
507 // Fetch our decryption key |
|
508 |
|
509 $aes_key = $this->fetch_public_key($aes_key); |
|
510 if(!$aes_key) |
|
511 return 'Couldn\'t look up public key "'.$aes_key.'" for decryption'; |
|
512 |
|
513 // Convert the key to a binary string |
|
514 $bin_key = hexdecode($aes_key); |
|
515 |
|
516 if(strlen($bin_key) != AES_BITS / 8) |
|
517 return 'The decryption key is the wrong length'; |
|
518 |
|
519 // Decrypt our password |
|
520 $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX); |
|
521 |
|
522 // Initialize our success switch |
|
523 $success = false; |
|
524 |
|
525 // Select the user data from the table, and decrypt that so we can verify the password |
|
526 $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';'); |
|
527 if($db->numrows() < 1) |
|
528 return 'The username and/or password is incorrect.'; |
|
529 $row = $db->fetchrow(); |
|
530 |
|
531 // Check to see if we're logging in using a temporary password |
|
532 |
|
533 if((intval($row['temp_password_time']) + 3600*24) > time() ) |
|
534 { |
|
535 $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); |
|
536 if( $temp_pass == $password ) |
|
537 { |
|
538 $url = makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']); |
|
539 redirect($url, 'Login sucessful', 'Please wait while you are transferred to the Password Reset form.'); |
|
540 exit; |
|
541 } |
|
542 } |
|
543 |
|
544 if($row['old_encryption'] == 1) |
|
545 { |
|
546 // The user's password is stored using the obsolete and insecure MD5 algorithm, so we'll update the field with the new password |
|
547 if(md5($password) == $row['password']) |
|
548 { |
|
549 $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); |
|
550 $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); |
|
551 $success = true; |
|
552 } |
|
553 } |
|
554 else |
|
555 { |
|
556 // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match; if so then do challenge authentication |
|
557 $real_pass = $aes->decrypt(hexdecode($row['password']), $this->private_key, ENC_BINARY); |
|
558 if($password == $real_pass) |
|
559 { |
|
560 // Yay! We passed AES authentication, now do an MD5 challenge check to make sure we weren't spoofed |
|
561 $chal = substr($challenge, 0, 32); |
|
562 $salt = substr($challenge, 32, 32); |
|
563 $correct_challenge = md5( $real_pass . $salt ); |
|
564 if($chal == $correct_challenge) |
|
565 $success = true; |
|
566 } |
|
567 } |
|
568 if($success) |
|
569 { |
|
570 if($level > $row['user_level']) |
|
571 return 'You are not authorized for this level of access.'; |
|
572 |
|
573 $sess = $this->register_session(intval($row['user_id']), $username, $password, $level); |
|
574 if($sess) |
|
575 { |
|
576 $this->username = $username; |
|
577 $this->user_id = intval($row['user_id']); |
|
578 $this->theme = $row['theme']; |
|
579 $this->style = $row['style']; |
|
580 |
|
581 if($level > USER_LEVEL_MEMBER) |
|
582 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); |
|
583 else |
|
584 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); |
|
585 |
|
586 $code = $plugins->setHook('login_success'); |
|
587 foreach ( $code as $cmd ) |
|
588 { |
|
589 eval($cmd); |
|
590 } |
|
591 return 'success'; |
|
592 } |
|
593 else |
|
594 return 'Your login credentials were correct, but an internal error occurred while registering the session key in the database.'; |
|
595 } |
|
596 else |
|
597 { |
|
598 if($level > USER_LEVEL_MEMBER) |
|
599 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); |
|
600 else |
|
601 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); |
|
602 |
|
603 return 'The username and/or password is incorrect.'; |
|
604 } |
|
605 } |
|
606 |
|
607 /** |
|
608 * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript |
|
609 * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox |
|
610 * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication |
|
611 * @param string $username The username |
|
612 * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true |
|
613 * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false. |
|
614 * @param int $level The privilege level we're authenticating for, defaults to 0 |
|
615 */ |
|
616 |
|
617 function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER) |
|
618 { |
|
619 global $db, $session, $paths, $template, $plugins; // Common objects |
|
620 |
|
621 $pass_hashed = ( $already_md5ed ) ? $password : md5($password); |
|
622 |
|
623 // Perhaps we're upgrading Enano? |
|
624 if($this->compat) |
|
625 { |
|
626 return $this->login_compat($username, $pass_hashed, $level); |
|
627 } |
|
628 |
|
629 // Instanciate the Rijndael encryption object |
|
630 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
631 |
|
632 // Initialize our success switch |
|
633 $success = false; |
|
634 |
|
635 // Retrieve the real password from the database |
|
636 $this->sql('SELECT password,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';'); |
|
637 if($db->numrows() < 1) |
|
638 return 'The username and/or password is incorrect.'; |
|
639 $row = $db->fetchrow(); |
|
640 |
|
641 // Check to see if we're logging in using a temporary password |
|
642 |
|
643 if((intval($row['temp_password_time']) + 3600*24) > time() ) |
|
644 { |
|
645 $temp_pass = $aes->decrypt( $row['temp_password'], $this->private_key, ENC_HEX ); |
|
646 if( md5($temp_pass) == $pass_hashed ) |
|
647 { |
|
648 header('Location: ' . makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $row['temp_password']) ); |
|
649 exit; |
|
650 } |
|
651 } |
|
652 |
|
653 if($row['old_encryption'] == 1) |
|
654 { |
|
655 // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password |
|
656 if($pass_hashed == $row['password'] && !$already_md5ed) |
|
657 { |
|
658 $pass_stashed = $aes->encrypt($password, $this->private_key, ENC_HEX); |
|
659 $this->sql('UPDATE '.table_prefix.'users SET password=\''.$pass_stashed.'\',old_encryption=0 WHERE user_id='.$row['user_id'].';'); |
|
660 $success = true; |
|
661 } |
|
662 elseif($pass_hashed == $row['password'] && $already_md5ed) |
|
663 { |
|
664 // We don't have the real password so don't bother with encrypting it, just call it success and get out of here |
|
665 $success = true; |
|
666 } |
|
667 } |
|
668 else |
|
669 { |
|
670 // Our password field is up-to-date with the >=1.0RC1 encryption standards, so decrypt the password in the table and see if we have a match |
|
671 $real_pass = $aes->decrypt($row['password'], $this->private_key); |
|
672 if($pass_hashed == md5($real_pass)) |
|
673 { |
|
674 $success = true; |
|
675 } |
|
676 } |
|
677 if($success) |
|
678 { |
|
679 if((int)$level > (int)$row['user_level']) |
|
680 return 'You are not authorized for this level of access.'; |
|
681 $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level); |
|
682 if($sess) |
|
683 { |
|
684 if($level > USER_LEVEL_MEMBER) |
|
685 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); |
|
686 else |
|
687 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); |
|
688 |
|
689 $code = $plugins->setHook('login_success'); |
|
690 foreach ( $code as $cmd ) |
|
691 { |
|
692 eval($cmd); |
|
693 } |
|
694 return 'success'; |
|
695 } |
|
696 else |
|
697 return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.'; |
|
698 } |
|
699 else |
|
700 { |
|
701 if($level > USER_LEVEL_MEMBER) |
|
702 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')'); |
|
703 else |
|
704 $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')'); |
|
705 |
|
706 return 'The username and/or password is incorrect.'; |
|
707 } |
|
708 } |
|
709 |
|
710 /** |
|
711 * Attempts to log in using the old table structure and algorithm. |
|
712 * @param string $username |
|
713 * @param string $password This should be an MD5 hash |
|
714 * @return string 'success' if successful, or error message on failure |
|
715 */ |
|
716 |
|
717 function login_compat($username, $password, $level = 0) |
|
718 { |
|
719 global $db, $session, $paths, $template, $plugins; // Common objects |
|
720 $pass_hashed =& $password; |
|
721 $this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';'); |
|
722 if($db->numrows() < 1) |
|
723 return 'The username and/or password is incorrect.'; |
|
724 $row = $db->fetchrow(); |
|
725 if($row['password'] == $password) |
|
726 { |
|
727 if((int)$level > (int)$row['user_level']) |
|
728 return 'You are not authorized for this level of access.'; |
|
729 $sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level); |
|
730 if($sess) |
|
731 return 'success'; |
|
732 else |
|
733 return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.'; |
|
734 } |
|
735 else |
|
736 { |
|
737 return 'The username and/or password is incorrect.'; |
|
738 } |
|
739 } |
|
740 |
|
741 /** |
|
742 * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated! |
|
743 * Basically the session key is a base64-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password]" |
|
744 * @param int $user_id |
|
745 * @param string $username |
|
746 * @param string $password |
|
747 * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER |
|
748 * @return bool |
|
749 */ |
|
750 |
|
751 function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER) |
|
752 { |
|
753 $salt = md5(microtime() . mt_rand()); |
|
754 $passha1 = sha1($password); |
|
755 $session_key = "u=$username;p=$passha1;s=$salt"; |
|
756 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
757 $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX); |
|
758 if($level > USER_LEVEL_MEMBER) |
|
759 { |
|
760 $hexkey = strrev($session_key); |
|
761 $this->sid_super = $hexkey; |
|
762 $_GET['auth'] = $hexkey; |
|
763 } |
|
764 else |
|
765 { |
|
766 setcookie( 'sid', $session_key, time()+315360000, scriptPath.'/' ); |
|
767 $_COOKIE['sid'] = $session_key; |
|
768 } |
|
769 $keyhash = md5($session_key); |
|
770 $ip = ip2hex($_SERVER['REMOTE_ADDR']); |
|
771 if(!$ip) |
|
772 die('$session->register_session: Remote-Addr was spoofed'); |
|
773 $time = time(); |
|
774 if(!is_int($user_id)) |
|
775 die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); |
|
776 if(!is_int($level)) |
|
777 die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); |
|
778 |
|
779 $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); |
|
780 return true; |
|
781 } |
|
782 |
|
783 /** |
|
784 * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this. |
|
785 * @see sessionManager::register_session() |
|
786 * @access private |
|
787 */ |
|
788 |
|
789 function register_session_compat($user_id, $username, $password, $level = 0) |
|
790 { |
|
791 $salt = md5(microtime() . mt_rand()); |
|
792 $thekey = md5($password . $salt); |
|
793 if($level > 0) |
|
794 { |
|
795 $this->sid_super = $thekey; |
|
796 } |
|
797 else |
|
798 { |
|
799 setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' ); |
|
800 $_COOKIE['sid'] = $thekey; |
|
801 } |
|
802 $ip = ip2hex($_SERVER['REMOTE_ADDR']); |
|
803 if(!$ip) |
|
804 die('$session->register_session: Remote-Addr was spoofed'); |
|
805 $time = time(); |
|
806 if(!is_int($user_id)) |
|
807 die('Somehow an SQL injection attempt crawled into our session registrar! (1)'); |
|
808 if(!is_int($level)) |
|
809 die('Somehow an SQL injection attempt crawled into our session registrar! (2)'); |
|
810 $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');'); |
|
811 return true; |
|
812 } |
|
813 |
|
814 /** |
|
815 * Creates/restores a guest session |
|
816 * @todo implement real session management for guests |
|
817 */ |
|
818 |
|
819 function register_guest_session() |
|
820 { |
|
821 global $db, $session, $paths, $template, $plugins; // Common objects |
|
822 $this->username = $_SERVER['REMOTE_ADDR']; |
|
823 $this->user_level = USER_LEVEL_GUEST; |
|
824 if($this->compat || defined('IN_ENANO_INSTALL')) |
|
825 { |
|
826 $this->theme = 'oxygen'; |
|
827 $this->style = 'bleu'; |
|
828 } |
|
829 else |
|
830 { |
|
831 $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme; |
|
832 $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : substr($template->named_theme_list[$this->theme]['default_style'], 0, strlen($template->named_theme_list[$this->theme]['default_style'])-4); |
|
833 } |
|
834 $this->user_id = 1; |
|
835 } |
|
836 |
|
837 /** |
|
838 * Validates a session key, and returns the userdata associated with the key or false |
|
839 * @param string $key The session key to validate |
|
840 * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides. |
|
841 */ |
|
842 |
|
843 function validate_session($key) |
|
844 { |
|
845 global $db, $session, $paths, $template, $plugins; // Common objects |
|
846 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE, true); |
|
847 $decrypted_key = $aes->decrypt($key, $this->private_key, ENC_HEX); |
|
848 |
|
849 if ( !$decrypted_key ) |
|
850 { |
|
851 die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>'); |
|
852 } |
|
853 |
|
854 $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=([A-Fa-f0-9]+?)$/', $decrypted_key, $keydata); |
|
855 if($n < 1) |
|
856 { |
|
857 // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key; |
|
858 return false; |
|
859 } |
|
860 $keyhash = md5($key); |
|
861 $salt = $db->escape($keydata[3]); |
|
862 $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,x.* FROM '.table_prefix.'session_keys AS k |
|
863 LEFT JOIN '.table_prefix.'users AS u |
|
864 ON ( u.user_id=k.user_id ) |
|
865 LEFT JOIN '.table_prefix.'users_extra AS x |
|
866 ON ( u.user_id=x.user_id OR x.user_id IS NULL ) |
|
867 LEFT JOIN '.table_prefix.'privmsgs AS p |
|
868 ON ( p.message_to=u.username AND p.message_read=0 ) |
|
869 WHERE k.session_key=\''.$keyhash.'\' |
|
870 AND k.salt=\''.$salt.'\' |
|
871 GROUP BY u.user_id;'); |
|
872 if($db->numrows() < 1) |
|
873 { |
|
874 // echo '(debug) $session->validate_session: Key was not found in database<br />'; |
|
875 return false; |
|
876 } |
|
877 $row = $db->fetchrow(); |
|
878 $row['user_id'] =& $row['uid']; |
|
879 $ip = ip2hex($_SERVER['REMOTE_ADDR']); |
|
880 if($row['auth_level'] > $row['user_level']) |
|
881 { |
|
882 // Failed authorization check |
|
883 // echo '(debug) $session->validate_session: access to this auth level denied<br />'; |
|
884 return false; |
|
885 } |
|
886 if($ip != $row['source_ip']) |
|
887 { |
|
888 // Failed IP address check |
|
889 // echo '(debug) $session->validate_session: IP address mismatch<br />'; |
|
890 return false; |
|
891 } |
|
892 |
|
893 // Do the password validation |
|
894 $real_pass = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); |
|
895 |
|
896 //die('<pre>'.print_r($keydata, true).'</pre>'); |
|
897 if(sha1($real_pass) != $keydata[2]) |
|
898 { |
|
899 // Failed password check |
|
900 // echo '(debug) $session->validate_session: encrypted password is wrong<br />Real password: '.$real_pass.'<br />Real hash: '.sha1($real_pass).'<br />User hash: '.$keydata[2]; |
|
901 return false; |
|
902 } |
|
903 |
|
904 $time_now = time(); |
|
905 $time_key = $row['time'] + 900; |
|
906 if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER) |
|
907 { |
|
908 // Session timed out |
|
909 // echo '(debug) $session->validate_session: super session timed out<br />'; |
|
910 $this->sw_timed_out = true; |
|
911 return false; |
|
912 } |
|
913 |
|
914 // If this is an elevated-access session key, update the time |
|
915 if( $row['auth_level'] > USER_LEVEL_MEMBER ) |
|
916 { |
|
917 $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.$keyhash.'\';'); |
|
918 } |
|
919 |
|
920 $row['password'] = md5($real_pass); |
|
921 return $row; |
|
922 } |
|
923 |
|
924 /** |
|
925 * Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system. |
|
926 * @param string $key The session key to validate |
|
927 * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides. |
|
928 */ |
|
929 |
|
930 function compat_validate_session($key) |
|
931 { |
|
932 global $db, $session, $paths, $template, $plugins; // Common objects |
|
933 $key = $db->escape($key); |
|
934 |
|
935 $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level FROM '.table_prefix.'session_keys AS k |
|
936 LEFT JOIN '.table_prefix.'users AS u |
|
937 ON u.user_id=k.user_id |
|
938 WHERE k.session_key=\''.$key.'\';'); |
|
939 if($db->numrows() < 1) |
|
940 { |
|
941 // echo '(debug) $session->validate_session: Key '.$key.' was not found in database<br />'; |
|
942 return false; |
|
943 } |
|
944 $row = $db->fetchrow(); |
|
945 $ip = ip2hex($_SERVER['REMOTE_ADDR']); |
|
946 if($row['auth_level'] > $row['user_level']) |
|
947 { |
|
948 // Failed authorization check |
|
949 // echo '(debug) $session->validate_session: user not authorized for this access level'; |
|
950 return false; |
|
951 } |
|
952 if($ip != $row['source_ip']) |
|
953 { |
|
954 // Failed IP address check |
|
955 // echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.''; |
|
956 return false; |
|
957 } |
|
958 |
|
959 // Do the password validation |
|
960 $real_key = md5($row['password'] . $row['salt']); |
|
961 |
|
962 //die('<pre>'.print_r($keydata, true).'</pre>'); |
|
963 if($real_key != $key) |
|
964 { |
|
965 // Failed password check |
|
966 // echo '(debug) $session->validate_session: supplied password is wrong<br />Real key: '.$real_key.'<br />User key: '.$key; |
|
967 return false; |
|
968 } |
|
969 |
|
970 $time_now = time(); |
|
971 $time_key = $row['time'] + 900; |
|
972 if($time_now > $time_key && $row['auth_level'] >= 1) |
|
973 { |
|
974 $this->sw_timed_out = true; |
|
975 // Session timed out |
|
976 // echo '(debug) $session->validate_session: super session timed out<br />'; |
|
977 return false; |
|
978 } |
|
979 |
|
980 return $row; |
|
981 } |
|
982 |
|
983 /** |
|
984 * Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level |
|
985 * @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER |
|
986 * @return string 'success' if successful, or error on failure |
|
987 */ |
|
988 |
|
989 function logout($level = USER_LEVEL_MEMBER) |
|
990 { |
|
991 global $db, $session, $paths, $template, $plugins; // Common objects |
|
992 $ou = $this->username; |
|
993 $oid = $this->user_id; |
|
994 if($level > USER_LEVEL_CHPREF) |
|
995 { |
|
996 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
997 if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD) return 'success'; |
|
998 // Destroy elevated privileges |
|
999 $keyhash = md5(strrev($this->sid_super)); |
|
1000 $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';'); |
|
1001 $this->sid_super = false; |
|
1002 $this->auth_level = USER_LEVEL_MEMBER; |
|
1003 } |
|
1004 else |
|
1005 { |
|
1006 if($this->user_logged_in) |
|
1007 { |
|
1008 // Completely destroy our session |
|
1009 if($this->auth_level > USER_LEVEL_CHPREF) |
|
1010 { |
|
1011 $this->logout(USER_LEVEL_ADMIN); |
|
1012 } |
|
1013 $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';'); |
|
1014 setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' ); |
|
1015 } |
|
1016 } |
|
1017 $code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid)); |
|
1018 foreach ( $code as $cmd ) |
|
1019 { |
|
1020 eval($cmd); |
|
1021 } |
|
1022 return 'success'; |
|
1023 } |
|
1024 |
|
1025 # Miscellaneous stuff |
|
1026 |
|
1027 /** |
|
1028 * Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff |
|
1029 * @param string $url The URL to add session data to |
|
1030 * @return string |
|
1031 */ |
|
1032 |
|
1033 function append_sid($url) |
|
1034 { |
|
1035 $sep = ( strstr($url, '?') ) ? '&' : '?'; |
|
1036 if ( $this->sid_super ) |
|
1037 { |
|
1038 $url = $url . $sep . 'auth=' . urlencode($this->sid_super); |
|
1039 // echo($this->sid_super.'<br/>'); |
|
1040 } |
|
1041 return $url; |
|
1042 } |
|
1043 |
|
1044 /** |
|
1045 * Grabs the user's password MD5 |
|
1046 * @return string, or bool false if access denied |
|
1047 */ |
|
1048 |
|
1049 function grab_password_hash() |
|
1050 { |
|
1051 if(!$this->password_hash) return false; |
|
1052 return $this->password_hash; |
|
1053 } |
|
1054 |
|
1055 /** |
|
1056 * Destroys the user's password MD5 in memory |
|
1057 */ |
|
1058 |
|
1059 function disallow_password_grab() |
|
1060 { |
|
1061 $this->password_hash = false; |
|
1062 return false; |
|
1063 } |
|
1064 |
|
1065 /** |
|
1066 * Generates an AES key and stashes it in the database |
|
1067 * @return string Hex-encoded AES key |
|
1068 */ |
|
1069 |
|
1070 function rijndael_genkey() |
|
1071 { |
|
1072 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
1073 $key = $aes->gen_readymade_key(); |
|
1074 $keys = getConfig('login_key_cache'); |
|
1075 if(is_string($keys)) |
|
1076 $keys .= $key; |
|
1077 else |
|
1078 $keys = $key; |
|
1079 setConfig('login_key_cache', $keys); |
|
1080 return $key; |
|
1081 } |
|
1082 |
|
1083 /** |
|
1084 * Generate a totally random 128-bit value for MD5 challenges |
|
1085 * @return string |
|
1086 */ |
|
1087 |
|
1088 function dss_rand() |
|
1089 { |
|
1090 $aes = new AESCrypt(); |
|
1091 $random = $aes->randkey(128); |
|
1092 unset($aes); |
|
1093 return md5(microtime() . $random); |
|
1094 } |
|
1095 |
|
1096 /** |
|
1097 * Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed. |
|
1098 * @param string $md5 The MD5 sum of the key |
|
1099 * @return string, or bool false on failure |
|
1100 */ |
|
1101 |
|
1102 function fetch_public_key($md5) |
|
1103 { |
|
1104 $keys = getConfig('login_key_cache'); |
|
1105 $keys = enano_str_split($keys, AES_BITS / 4); |
|
1106 |
|
1107 foreach($keys as $i => $k) |
|
1108 { |
|
1109 if(md5($k) == $md5) |
|
1110 { |
|
1111 unset($keys[$i]); |
|
1112 if(count($keys) > 0) |
|
1113 { |
|
1114 if ( strlen(getConfig('login_key_cache') ) > 64000 ) |
|
1115 { |
|
1116 // This should only need to be done once every month or so for an average-size site |
|
1117 setConfig('login_key_cache', ''); |
|
1118 } |
|
1119 else |
|
1120 { |
|
1121 $keys = implode('', array_values($keys)); |
|
1122 setConfig('login_key_cache', $keys); |
|
1123 } |
|
1124 } |
|
1125 else |
|
1126 { |
|
1127 setConfig('login_key_cache', ''); |
|
1128 } |
|
1129 return $k; |
|
1130 } |
|
1131 } |
|
1132 // Couldn't find the key... |
|
1133 return false; |
|
1134 } |
|
1135 |
|
1136 /** |
|
1137 * Adds a user to a group. |
|
1138 * @param int User ID |
|
1139 * @param int Group ID |
|
1140 * @param bool Group moderator - defaults to false |
|
1141 * @return bool True on success, false on failure |
|
1142 */ |
|
1143 |
|
1144 function add_user_to_group($user_id, $group_id, $is_mod = false) |
|
1145 { |
|
1146 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1147 |
|
1148 // Validation |
|
1149 if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) ) |
|
1150 return false; |
|
1151 if ( $user_id < 1 || $group_id < 1 ) |
|
1152 return false; |
|
1153 |
|
1154 $mod_switch = ( $is_mod ) ? '1' : '0'; |
|
1155 $q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';'); |
|
1156 if ( !$q ) |
|
1157 $db->_die(); |
|
1158 if ( $db->numrows() < 1 ) |
|
1159 { |
|
1160 // User is not in group |
|
1161 $this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');'); |
|
1162 return true; |
|
1163 } |
|
1164 else |
|
1165 { |
|
1166 $row = $db->fetchrow(); |
|
1167 // Update modship status |
|
1168 if ( strval($row['is_mod']) == $mod_switch ) |
|
1169 { |
|
1170 // Modship unchanged |
|
1171 return true; |
|
1172 } |
|
1173 else |
|
1174 { |
|
1175 // Modship changed |
|
1176 $this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';'); |
|
1177 return true; |
|
1178 } |
|
1179 } |
|
1180 return false; |
|
1181 } |
|
1182 |
|
1183 /** |
|
1184 * Removes a user from a group. |
|
1185 * @param int User ID |
|
1186 * @param int Group ID |
|
1187 * @return bool True on success, false on failure |
|
1188 * @todo put a little more error checking in... |
|
1189 */ |
|
1190 |
|
1191 function remove_user_from_group($user_id, $group_id) |
|
1192 { |
|
1193 if ( !is_int($user_id) || !is_int($group_id) ) |
|
1194 return false; |
|
1195 $this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;"); |
|
1196 return true; |
|
1197 } |
|
1198 |
|
1199 /** |
|
1200 * Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned. |
|
1201 */ |
|
1202 |
|
1203 function check_banlist() |
|
1204 { |
|
1205 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1206 if($this->compat) |
|
1207 $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;'); |
|
1208 else |
|
1209 $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex,reason FROM '.table_prefix.'banlist ORDER BY ban_type;'); |
|
1210 if(!$q) $db->_die('The banlist data could not be selected.'); |
|
1211 $banned = false; |
|
1212 while($row = $db->fetchrow()) |
|
1213 { |
|
1214 if($this->compat) |
|
1215 $row['reason'] = 'None available - session manager is in compatibility mode'; |
|
1216 switch($row['ban_type']) |
|
1217 { |
|
1218 case BAN_IP: |
|
1219 if(intval($row['is_regex'])==1) { |
|
1220 if(preg_match('#'.$row['ban_value'].'#i', $_SERVER['REMOTE_ADDR'])) |
|
1221 { |
|
1222 $banned = true; |
|
1223 $reason = $row['reason']; |
|
1224 } |
|
1225 } |
|
1226 else { |
|
1227 if($row['ban_value']==$_SERVER['REMOTE_ADDR']) { $banned = true; $reason = $row['reason']; } |
|
1228 } |
|
1229 break; |
|
1230 case BAN_USER: |
|
1231 if(intval($row['is_regex'])==1) { |
|
1232 if(preg_match('#'.$row['ban_value'].'#i', $this->username)) |
|
1233 { |
|
1234 $banned = true; |
|
1235 $reason = $row['reason']; |
|
1236 } |
|
1237 } |
|
1238 else { |
|
1239 if($row['ban_value']==$this->username) { $banned = true; $reason = $row['reason']; } |
|
1240 } |
|
1241 break; |
|
1242 case BAN_EMAIL: |
|
1243 if(intval($row['is_regex'])==1) { |
|
1244 if(preg_match('#'.$row['ban_value'].'#i', $this->email)) |
|
1245 { |
|
1246 $banned = true; |
|
1247 $reason = $row['reason']; |
|
1248 } |
|
1249 } |
|
1250 else { |
|
1251 if($row['ban_value']==$this->email) { $banned = true; $reason = $row['reason']; } |
|
1252 } |
|
1253 break; |
|
1254 default: |
|
1255 die('Ban error: rule "'.$row['ban_value'].'" has an invalid type ('.$row['ban_type'].')'); |
|
1256 } |
|
1257 } |
|
1258 if($banned && $paths->get_pageid_from_url() != $paths->nslist['Special'].'CSS') |
|
1259 { |
|
1260 // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it |
|
1261 die_semicritical('Ban notice', '<div class="error-box">You have been banned from this website. Please contact the site administrator for more information.<br /><br />Reason:<br />'.$reason.'</div>'); |
|
1262 exit; |
|
1263 } |
|
1264 } |
|
1265 |
|
1266 # Registration |
|
1267 |
|
1268 /** |
|
1269 * Registers a user. This does not perform any type of login. |
|
1270 * @param string $username |
|
1271 * @param string $password This should be unencrypted. |
|
1272 * @param string $email |
|
1273 * @param string $real_name Optional, defaults to ''. |
|
1274 */ |
|
1275 |
|
1276 function create_user($username, $password, $email, $real_name = '') { |
|
1277 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1278 |
|
1279 // Initialize AES |
|
1280 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
1281 |
|
1282 if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.'; |
|
1283 $username = $this->prepare_text($username); |
|
1284 $email = $this->prepare_text($email); |
|
1285 $real_name = $this->prepare_text($real_name); |
|
1286 $password = $aes->encrypt($password, $this->private_key, ENC_HEX); |
|
1287 |
|
1288 $nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : ''; |
|
1289 $q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE lcase(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';'); |
|
1290 if($db->numrows() > 0) { |
|
1291 $r = 'The '; |
|
1292 $i=0; |
|
1293 $row = $db->fetchrow(); |
|
1294 // Wow! An error checker that actually speaks English with the properest grammar! :-P |
|
1295 if($row['username'] == $username) { $r .= 'username'; $i++; } |
|
1296 if($row['email'] == $email) { if($i) $r.=', '; $r .= 'e-mail address'; $i++; } |
|
1297 if($row['real_name'] == $real_name && $real_name != '') { if($i) $r.=', and '; $r .= 'real name'; $i++; } |
|
1298 $r .= ' that you entered '; |
|
1299 $r .= ( $i == 1 ) ? 'is' : 'are'; |
|
1300 $r .= ' already in use by another user.'; |
|
1301 return $r; |
|
1302 } |
|
1303 |
|
1304 // Require the account to be activated? |
|
1305 switch(getConfig('account_activation')) |
|
1306 { |
|
1307 case 'none': |
|
1308 default: |
|
1309 $active = '1'; |
|
1310 break; |
|
1311 case 'user': |
|
1312 $active = '0'; |
|
1313 break; |
|
1314 case 'admin': |
|
1315 $active = '0'; |
|
1316 break; |
|
1317 } |
|
1318 |
|
1319 // Generate a totally random activation key |
|
1320 $actkey = sha1 ( microtime() . mt_rand() ); |
|
1321 |
|
1322 // We good, create the user |
|
1323 $this->sql('INSERT INTO '.table_prefix.'users ( username, password, email, real_name, theme, style, reg_time, account_active, activation_key, user_level ) VALUES ( \''.$username.'\', \''.$password.'\', \''.$email.'\', \''.$real_name.'\', \''.$template->default_theme.'\', \''.$template->default_style.'\', '.time().', '.$active.', \''.$actkey.'\', '.USER_LEVEL_CHPREF.' )'); |
|
1324 |
|
1325 // Require the account to be activated? |
|
1326 switch(getConfig('account_activation')) |
|
1327 { |
|
1328 case 'none': |
|
1329 default: |
|
1330 break; |
|
1331 case 'user': |
|
1332 $a = $this->send_activation_mail($username); |
|
1333 if(!$a) |
|
1334 { |
|
1335 $this->admin_activation_request($username); |
|
1336 return 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you. ' . $a; |
|
1337 } |
|
1338 break; |
|
1339 case 'admin': |
|
1340 $this->admin_activation_request($username); |
|
1341 break; |
|
1342 } |
|
1343 |
|
1344 // Leave some data behind for the hook |
|
1345 $code = $plugins->setHook('user_registered'); // , Array('username'=>$username)); |
|
1346 foreach ( $code as $cmd ) |
|
1347 { |
|
1348 eval($cmd); |
|
1349 } |
|
1350 |
|
1351 // $this->register_session($username, $password); |
|
1352 return 'success'; |
|
1353 } |
|
1354 |
|
1355 /** |
|
1356 * Attempts to send an e-mail to the specified user with activation instructions. |
|
1357 * @param string $u The usernamd of the user requesting activation |
|
1358 * @return bool true on success, false on failure |
|
1359 */ |
|
1360 |
|
1361 function send_activation_mail($u, $actkey = false) |
|
1362 { |
|
1363 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1364 $q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=1 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;'); |
|
1365 $un = $db->fetchrow(); |
|
1366 $admin_user = $un['username']; |
|
1367 $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';'); |
|
1368 $r = $db->fetchrow(); |
|
1369 if ( empty($r['email']) ) |
|
1370 $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row'); |
|
1371 $message = 'Dear '.$u.', |
|
1372 Thank you for registering on '.getConfig('site_name').'. Your account creation is almost complete. To complete the registration process, please click the following link or paste it into your web browser: |
|
1373 |
|
1374 '; |
|
1375 if(isset($_SERVER['HTTPS'])) $prot = 'https'; |
|
1376 else $prot = 'http'; |
|
1377 if($_SERVER['SERVER_PORT'] == '80') $p = ''; |
|
1378 else $p = ':'.$_SERVER['SERVER_PORT']; |
|
1379 $sidbak = false; |
|
1380 if($this->sid_super) |
|
1381 $sidbak = $this->sid_super; |
|
1382 $this->sid_super = false; |
|
1383 $aklink = makeUrlNS('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) ); |
|
1384 if($sidbak) |
|
1385 $this->sid_super = $sidbak; |
|
1386 unset($sidbak); |
|
1387 $message .= "$prot://".$_SERVER['HTTP_HOST'].$p.$aklink; |
|
1388 $message .= "\n\nSincerely yours, \n$admin_user and the ".$_SERVER['HTTP_HOST']." administration team"; |
|
1389 error_reporting(E_ALL); |
|
1390 dc_dump($r, 'session: about to send activation e-mail to '.$r['email']); |
|
1391 if(getConfig('smtp_enabled') == '1') |
|
1392 { |
|
1393 $result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email')); |
|
1394 if($result == 'success') $result = true; |
|
1395 else { echo $result; $result = false; } |
|
1396 } else { |
|
1397 $result = mail($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email')); |
|
1398 } |
|
1399 return $result; |
|
1400 } |
|
1401 |
|
1402 /** |
|
1403 * Sends an e-mail to a user so they can reset their password. |
|
1404 * @param int $user The user ID, or username if it's a string |
|
1405 * @return bool true on success, false on failure |
|
1406 */ |
|
1407 |
|
1408 function mail_password_reset($user) |
|
1409 { |
|
1410 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1411 if(is_int($user)) |
|
1412 { |
|
1413 $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer |
|
1414 } |
|
1415 elseif(is_string($user)) |
|
1416 { |
|
1417 $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($user).'\';'); |
|
1418 } |
|
1419 else |
|
1420 { |
|
1421 return false; |
|
1422 } |
|
1423 |
|
1424 $row = $db->fetchrow(); |
|
1425 $temp_pass = $this->random_pass(); |
|
1426 |
|
1427 $this->register_temp_password($row['user_id'], $temp_pass); |
|
1428 |
|
1429 $site_name = getConfig('site_name'); |
|
1430 |
|
1431 $message = "Dear {$row['username']}, |
|
1432 |
|
1433 Someone (hopefully you) on the {$site_name} website requested that a new password be created. |
|
1434 |
|
1435 The request was sent from the IP address {$_SERVER['REMOTE_ADDR']}. |
|
1436 |
|
1437 If you did not request the new password, then you do not need to do anything; the password will be invalidated after 24 hours. |
|
1438 |
|
1439 If you did request this password, then please log in using the password shown below: |
|
1440 |
|
1441 Password: {$temp_pass} |
|
1442 |
|
1443 After you log in using this password, you will be able to reset your real password. You can only log in using this temporary password once. |
|
1444 |
|
1445 Sincerely yours, |
|
1446 The {$site_name} administration team |
|
1447 "; |
|
1448 |
|
1449 if(getConfig('smtp_enabled') == '1') |
|
1450 { |
|
1451 $result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email')); |
|
1452 if($result == 'success') |
|
1453 { |
|
1454 $result = true; |
|
1455 } |
|
1456 else |
|
1457 { |
|
1458 echo '<p>'.$result.'</p>'; |
|
1459 $result = false; |
|
1460 } |
|
1461 } else { |
|
1462 $result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email')); |
|
1463 } |
|
1464 return $result; |
|
1465 } |
|
1466 |
|
1467 /** |
|
1468 * Sets the temporary password for the specified user to whatever is specified. |
|
1469 * @param int $user_id |
|
1470 * @param string $password |
|
1471 * @return bool |
|
1472 */ |
|
1473 |
|
1474 function register_temp_password($user_id, $password) |
|
1475 { |
|
1476 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
1477 $temp_pass = $aes->encrypt($password, $this->private_key, ENC_HEX); |
|
1478 $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';'); |
|
1479 } |
|
1480 |
|
1481 /** |
|
1482 * Sends a request to the admin panel to have the username $u activated. |
|
1483 * @param string $u The username of the user requesting activation |
|
1484 */ |
|
1485 |
|
1486 function admin_activation_request($u) |
|
1487 { |
|
1488 global $db; |
|
1489 $this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.date('d M Y h:i a').'\', \''.$this->username.'\', \''.$db->escape($u).'\');'); |
|
1490 } |
|
1491 |
|
1492 /** |
|
1493 * Activates a user account. If the action fails, a report is sent to the admin. |
|
1494 * @param string $user The username of the user requesting activation |
|
1495 * @param string $key The activation key |
|
1496 */ |
|
1497 |
|
1498 function activate_account($user, $key) |
|
1499 { |
|
1500 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1501 $this->sql('UPDATE '.table_prefix.'users SET account_active=1 WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';'); |
|
1502 $r = mysql_affected_rows(); |
|
1503 if ( $r > 0 ) |
|
1504 { |
|
1505 $e = $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')'); |
|
1506 } |
|
1507 else |
|
1508 { |
|
1509 $e = $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')'); |
|
1510 } |
|
1511 return $r; |
|
1512 } |
|
1513 |
|
1514 /** |
|
1515 * For a given user level identifier (USER_LEVEL_*), returns a string describing that user level. |
|
1516 * @param int User level |
|
1517 * @return string |
|
1518 */ |
|
1519 |
|
1520 function userlevel_to_string($user_level) |
|
1521 { |
|
1522 switch ( $user_level ) |
|
1523 { |
|
1524 case USER_LEVEL_GUEST: |
|
1525 return 'Low - guest privileges'; |
|
1526 case USER_LEVEL_MEMBER: |
|
1527 return 'Standard - normal member level'; |
|
1528 case USER_LEVEL_CHPREF: |
|
1529 return 'Medium - user can change his/her own e-mail address and password'; |
|
1530 case USER_LEVEL_MOD: |
|
1531 return 'High - moderator privileges'; |
|
1532 case USER_LEVEL_ADMIN: |
|
1533 return 'Highest - administrative privileges'; |
|
1534 default: |
|
1535 return "Unknown ($user_level)"; |
|
1536 } |
|
1537 } |
|
1538 |
|
1539 /** |
|
1540 * Updates a user's information in the database. Note that any of the values except $user_id can be false if you want to preserve the old values. |
|
1541 * @param int $user_id The user ID of the user to update - this cannot be changed |
|
1542 * @param string $username The new username |
|
1543 * @param string $old_pass The current password - only required if sessionManager::$user_level < USER_LEVEL_ADMIN. This should usually be an UNENCRYPTED string. This can also be an array - if it is, key 0 is treated as data AES-encrypted with key 1 |
|
1544 * @param string $password The new password |
|
1545 * @param string $email The new e-mail address |
|
1546 * @param string $realname The new real name |
|
1547 * @param string $signature The updated forum/comment signature |
|
1548 * @param int $user_level The updated user level |
|
1549 * @return string 'success' if successful, or array of error strings on failure |
|
1550 */ |
|
1551 |
|
1552 function update_user($user_id, $username = false, $old_pass = false, $password = false, $email = false, $realname = false, $signature = false, $user_level = false) |
|
1553 { |
|
1554 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1555 |
|
1556 // Create some arrays |
|
1557 |
|
1558 $errors = Array(); // Used to hold error strings |
|
1559 $strs = Array(); // Sub-query statements |
|
1560 |
|
1561 // Scan the user ID for problems |
|
1562 if(intval($user_id) < 1) $errors[] = 'SQL injection attempt'; |
|
1563 |
|
1564 // Instanciate the AES encryption class |
|
1565 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
1566 |
|
1567 // If all of our input vars are false, then we've effectively done our job so get out of here |
|
1568 if($username === false && $password === false && $email === false && $realname === false && $signature === false && $user_level === false) |
|
1569 { |
|
1570 // echo 'debug: $session->update_user(): success (no changes requested)'; |
|
1571 return 'success'; |
|
1572 } |
|
1573 |
|
1574 // Initialize our authentication check |
|
1575 $authed = false; |
|
1576 |
|
1577 // Verify the inputted password |
|
1578 if(is_string($old_pass)) |
|
1579 { |
|
1580 $q = $this->sql('SELECT password FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); |
|
1581 if($db->numrows() < 1) |
|
1582 { |
|
1583 $errors[] = 'The password data could not be selected for verification.'; |
|
1584 } |
|
1585 else |
|
1586 { |
|
1587 $row = $db->fetchrow(); |
|
1588 $real = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); |
|
1589 if($real == $old_pass) |
|
1590 $authed = true; |
|
1591 } |
|
1592 } |
|
1593 |
|
1594 elseif(is_array($old_pass)) |
|
1595 { |
|
1596 $old_pass = $aes->decrypt($old_pass[0], $old_pass[1]); |
|
1597 $q = $this->sql('SELECT password FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); |
|
1598 if($db->numrows() < 1) |
|
1599 { |
|
1600 $errors[] = 'The password data could not be selected for verification.'; |
|
1601 } |
|
1602 else |
|
1603 { |
|
1604 $row = $db->fetchrow(); |
|
1605 $real = $aes->decrypt($row['password'], $this->private_key, ENC_HEX); |
|
1606 if($real == $old_pass) |
|
1607 $authed = true; |
|
1608 } |
|
1609 } |
|
1610 |
|
1611 // Initialize our query |
|
1612 $q = 'UPDATE '.table_prefix.'users SET '; |
|
1613 |
|
1614 if($this->auth_level >= USER_LEVEL_ADMIN || $authed) // Need the current password in order to update the e-mail address, change the username, or reset the password |
|
1615 { |
|
1616 // Username |
|
1617 if(is_string($username)) |
|
1618 { |
|
1619 // Check the username for problems |
|
1620 if(!preg_match('#^'.$this->valid_username.'$#', $username)) |
|
1621 $errors[] = 'The username you entered contains invalid characters.'; |
|
1622 $strs[] = 'username=\''.$db->escape($username).'\''; |
|
1623 } |
|
1624 // Password |
|
1625 if(is_string($password) && strlen($password) >= 6) |
|
1626 { |
|
1627 // Password needs to be encrypted before being stashed |
|
1628 $encpass = $aes->encrypt($password, $this->private_key, ENC_HEX); |
|
1629 if(!$encpass) |
|
1630 $errors[] = 'The password could not be encrypted due to an internal error.'; |
|
1631 $strs[] = 'password=\''.$encpass.'\''; |
|
1632 } |
|
1633 // E-mail addy |
|
1634 if(is_string($email)) |
|
1635 { |
|
1636 // I didn't write this regex. |
|
1637 if(!preg_match('/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $email)) |
|
1638 $errors[] = 'The e-mail address you entered is invalid.'; |
|
1639 $strs[] = 'email=\''.$db->escape($email).'\''; |
|
1640 } |
|
1641 } |
|
1642 // Real name |
|
1643 if(is_string($realname)) |
|
1644 { |
|
1645 $strs[] = 'real_name=\''.$db->escape($realname).'\''; |
|
1646 } |
|
1647 // Forum/comment signature |
|
1648 if(is_string($signature)) |
|
1649 { |
|
1650 $strs[] = 'signature=\''.$db->escape($signature).'\''; |
|
1651 } |
|
1652 // User level |
|
1653 if(is_int($user_level)) |
|
1654 { |
|
1655 $strs[] = 'user_level='.$user_level; |
|
1656 } |
|
1657 |
|
1658 // Add our generated query to the query string |
|
1659 $q .= implode(',', $strs); |
|
1660 |
|
1661 // One last error check |
|
1662 if(sizeof($strs) < 1) $errors[] = 'An internal error occured building the SQL query, this is a bug'; |
|
1663 if(sizeof($errors) > 0) return $errors; |
|
1664 |
|
1665 // Free our temp arrays |
|
1666 unset($strs, $errors); |
|
1667 |
|
1668 // Finalize the query and run it |
|
1669 $q .= ' WHERE user_id='.$user_id.';'; |
|
1670 $this->sql($q); |
|
1671 |
|
1672 // We also need to trigger re-activation. |
|
1673 if ( is_string($email) ) |
|
1674 { |
|
1675 switch(getConfig('account_activation')) |
|
1676 { |
|
1677 case 'user': |
|
1678 case 'admin': |
|
1679 |
|
1680 if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' ) |
|
1681 // Don't require re-activation by admins for admins |
|
1682 break; |
|
1683 |
|
1684 // retrieve username |
|
1685 if ( !$username ) |
|
1686 { |
|
1687 $q = $this->sql('SELECT username FROM '.table_prefix.'users WHERE user_id='.$user_id.';'); |
|
1688 if($db->numrows() < 1) |
|
1689 { |
|
1690 $errors[] = 'The username could not be selected.'; |
|
1691 } |
|
1692 else |
|
1693 { |
|
1694 $row = $db->fetchrow(); |
|
1695 $username = $row['username']; |
|
1696 } |
|
1697 } |
|
1698 if ( !$username ) |
|
1699 return $errors; |
|
1700 |
|
1701 // Generate a totally random activation key |
|
1702 $actkey = sha1 ( microtime() . mt_rand() ); |
|
1703 $a = $this->send_activation_mail($username, $actkey); |
|
1704 if(!$a) |
|
1705 { |
|
1706 $this->admin_activation_request($username); |
|
1707 } |
|
1708 // Deactivate the account until e-mail is confirmed |
|
1709 $q = $db->sql_query('UPDATE '.table_prefix.'users SET account_active=0,activation_key=\'' . $actkey . '\' WHERE user_id=' . $user_id . ';'); |
|
1710 break; |
|
1711 } |
|
1712 } |
|
1713 |
|
1714 // Yay! We're done |
|
1715 return 'success'; |
|
1716 } |
|
1717 |
|
1718 # |
|
1719 # Access Control Lists |
|
1720 # |
|
1721 |
|
1722 /** |
|
1723 * Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used. |
|
1724 * @param string $acl_type An identifier for this field |
|
1725 * @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs. |
|
1726 * @param string $desc A human readable name for the permission type |
|
1727 * @param array $deps The list of dependencies - this should be an array of ACL types |
|
1728 * @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All". |
|
1729 */ |
|
1730 |
|
1731 function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All') |
|
1732 { |
|
1733 if(isset($this->acl_types[$acl_type])) |
|
1734 return false; |
|
1735 else |
|
1736 { |
|
1737 if(!$desc) |
|
1738 { |
|
1739 $desc = capitalize_first_letter(str_replace('_', ' ', $acl_type)); |
|
1740 } |
|
1741 $this->acl_types[$acl_type] = $default_perm; |
|
1742 $this->acl_descs[$acl_type] = $desc; |
|
1743 $this->acl_deps[$acl_type] = $deps; |
|
1744 $this->acl_scope[$acl_type] = explode('|', $scope); |
|
1745 } |
|
1746 return true; |
|
1747 } |
|
1748 |
|
1749 /** |
|
1750 * Tells us whether permission $type is allowed or not based on the current rules. |
|
1751 * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type()) |
|
1752 * @param bool $no_deps If true, disables dependency checking |
|
1753 * @return bool True if allowed, false if denied or if an error occured |
|
1754 */ |
|
1755 |
|
1756 function get_permissions($type, $no_deps = false) |
|
1757 { |
|
1758 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1759 if ( isset( $this->perms[$type] ) ) |
|
1760 { |
|
1761 if ( $this->perms[$type] == AUTH_DENY ) |
|
1762 $ret = false; |
|
1763 else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) |
|
1764 $ret = true; |
|
1765 else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) |
|
1766 $ret = false; |
|
1767 else if ( $this->perms[$type] == AUTH_ALLOW ) |
|
1768 $ret = true; |
|
1769 else if ( $this->perms[$type] == AUTH_DISALLOW ) |
|
1770 $ret = false; |
|
1771 } |
|
1772 else if(isset($this->acl_types[$type])) |
|
1773 { |
|
1774 if ( $this->acl_types[$type] == AUTH_DENY ) |
|
1775 $ret = false; |
|
1776 else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) |
|
1777 $ret = true; |
|
1778 else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) |
|
1779 $ret = false; |
|
1780 else if ( $this->acl_types[$type] == AUTH_ALLOW ) |
|
1781 $ret = true; |
|
1782 else if ( $this->acl_types[$type] == AUTH_DISALLOW ) |
|
1783 $ret = false; |
|
1784 } |
|
1785 else |
|
1786 { |
|
1787 // ACL type is undefined |
|
1788 trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING); |
|
1789 return false; // Be on the safe side and deny access |
|
1790 } |
|
1791 if ( !$no_deps ) |
|
1792 { |
|
1793 if ( !$this->acl_check_deps($type) ) |
|
1794 return false; |
|
1795 } |
|
1796 return $ret; |
|
1797 } |
|
1798 |
|
1799 /** |
|
1800 * Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method |
|
1801 * and several other abilities. |
|
1802 * @param string $page_id |
|
1803 * @param string $namespace |
|
1804 * @return object |
|
1805 */ |
|
1806 |
|
1807 function fetch_page_acl($page_id, $namespace) |
|
1808 { |
|
1809 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1810 |
|
1811 if ( count ( $this->acl_base_cache ) < 1 ) |
|
1812 { |
|
1813 // Permissions table not yet initialized |
|
1814 return false; |
|
1815 } |
|
1816 |
|
1817 //if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) ) |
|
1818 //{ |
|
1819 // // Page does not exist |
|
1820 // return false; |
|
1821 //} |
|
1822 |
|
1823 $object = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache ); |
|
1824 |
|
1825 return $object; |
|
1826 |
|
1827 } |
|
1828 |
|
1829 /** |
|
1830 * Read all of our permissions from the database and process/apply them. This should be called after the page is determined. |
|
1831 * @access private |
|
1832 */ |
|
1833 |
|
1834 function init_permissions() |
|
1835 { |
|
1836 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1837 // Initialize the permissions list with some defaults |
|
1838 $this->perms = $this->acl_types; |
|
1839 $this->acl_defaults_used = $this->perms; |
|
1840 |
|
1841 // Fetch sitewide defaults from the permissions table |
|
1842 $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE page_id IS NULL AND namespace IS NULL AND ( '; |
|
1843 |
|
1844 $q = Array(); |
|
1845 $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )'; |
|
1846 if(count($this->groups) > 0) |
|
1847 { |
|
1848 foreach($this->groups as $g_id => $g_name) |
|
1849 { |
|
1850 $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; |
|
1851 } |
|
1852 } |
|
1853 $bs .= implode(' OR ', $q) . ' ) ORDER BY target_type ASC, target_id ASC;'; |
|
1854 $q = $this->sql($bs); |
|
1855 if ( $row = $db->fetchrow() ) |
|
1856 { |
|
1857 do { |
|
1858 $rules = $this->string_to_perm($row['rules']); |
|
1859 $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 ); |
|
1860 $this->acl_merge_with_current($rules, $is_everyone); |
|
1861 } while ( $row = $db->fetchrow() ); |
|
1862 } |
|
1863 |
|
1864 // Eliminate types that don't apply to this namespace |
|
1865 foreach ( $this->perms AS $i => $perm ) |
|
1866 { |
|
1867 if ( !in_array ( $paths->namespace, $this->acl_scope[$i] ) && !in_array('All', $this->acl_scope[$i]) ) |
|
1868 { |
|
1869 unset($this->perms[$i]); |
|
1870 } |
|
1871 } |
|
1872 |
|
1873 // Cache the sitewide permissions for later use |
|
1874 $this->acl_base_cache = $this->perms; |
|
1875 |
|
1876 // Build a query to grab ACL info |
|
1877 $bs = 'SELECT rules,target_type,target_id FROM '.table_prefix.'acl WHERE ( '; |
|
1878 $q = Array(); |
|
1879 $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )'; |
|
1880 if(count($this->groups) > 0) |
|
1881 { |
|
1882 foreach($this->groups as $g_id => $g_name) |
|
1883 { |
|
1884 $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; |
|
1885 } |
|
1886 } |
|
1887 // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual |
|
1888 // permissions to override group permissions. |
|
1889 $bs .= implode(' OR ', $q) . ' ) AND ( page_id=\''.$db->escape($paths->cpage['urlname_nons']).'\' AND namespace=\''.$db->escape($paths->namespace).'\' ) |
|
1890 ORDER BY target_type ASC, page_id ASC, namespace ASC;'; |
|
1891 $q = $this->sql($bs); |
|
1892 if ( $row = $db->fetchrow() ) |
|
1893 { |
|
1894 do { |
|
1895 $rules = $this->string_to_perm($row['rules']); |
|
1896 $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 ); |
|
1897 $this->acl_merge_with_current($rules, $is_everyone); |
|
1898 } while ( $row = $db->fetchrow() ); |
|
1899 } |
|
1900 |
|
1901 } |
|
1902 |
|
1903 /** |
|
1904 * Extends the scope of a permission type. |
|
1905 * @param string The name of the permission type |
|
1906 * @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list. |
|
1907 * @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook |
|
1908 */ |
|
1909 |
|
1910 function acl_extend_scope($perm_type, $namespaces, &$p_in) |
|
1911 { |
|
1912 global $db, $session, $paths, $template, $plugins; // Common objects |
|
1913 $p_obj = ( is_object($p_in) ) ? $p_in : $paths; |
|
1914 $nslist = explode('|', $namespaces); |
|
1915 foreach ( $nslist as $i => $ns ) |
|
1916 { |
|
1917 if ( !isset($p_obj->nslist[$ns]) ) |
|
1918 { |
|
1919 unset($nslist[$i]); |
|
1920 } |
|
1921 else |
|
1922 { |
|
1923 $this->acl_scope[$perm_type][] = $ns; |
|
1924 if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) ) |
|
1925 { |
|
1926 $this->perms[$perm_type] = $this->acl_types[$perm_type]; |
|
1927 } |
|
1928 } |
|
1929 } |
|
1930 } |
|
1931 |
|
1932 /** |
|
1933 * Converts a permissions field into a string for database insertion. Similar in spirit to serialize(). |
|
1934 * @param array $perms An associative array with only integers as values |
|
1935 * @return string |
|
1936 */ |
|
1937 |
|
1938 function perm_to_string($perms) |
|
1939 { |
|
1940 $s = ''; |
|
1941 foreach($perms as $perm => $ac) |
|
1942 { |
|
1943 $s .= "$perm=$ac;"; |
|
1944 } |
|
1945 return $s; |
|
1946 } |
|
1947 |
|
1948 /** |
|
1949 * Converts a permissions string back to an array. |
|
1950 * @param string $perms The result from sessionManager::perm_to_string() |
|
1951 * @return array |
|
1952 */ |
|
1953 |
|
1954 function string_to_perm($perms) |
|
1955 { |
|
1956 $ret = Array(); |
|
1957 preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches); |
|
1958 foreach($matches[1] as $i => $t) |
|
1959 { |
|
1960 $ret[$t] = intval($matches[2][$i]); |
|
1961 } |
|
1962 return $ret; |
|
1963 } |
|
1964 |
|
1965 /** |
|
1966 * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails. |
|
1967 * @param array $perm1 The first set of permissions |
|
1968 * @param array $perm2 The second set of permissions |
|
1969 * @return array |
|
1970 */ |
|
1971 |
|
1972 function acl_merge($perm1, $perm2) |
|
1973 { |
|
1974 $ret = $perm1; |
|
1975 foreach ( $perm2 as $type => $level ) |
|
1976 { |
|
1977 if ( isset( $ret[$type] ) ) |
|
1978 { |
|
1979 if ( $ret[$type] != AUTH_DENY ) |
|
1980 $ret[$type] = $level; |
|
1981 } |
|
1982 // else |
|
1983 // { |
|
1984 // $ret[$type] = $level; |
|
1985 // } |
|
1986 } |
|
1987 return $ret; |
|
1988 } |
|
1989 |
|
1990 /** |
|
1991 * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not. |
|
1992 * @param array The array to merge into the master ACL list |
|
1993 * @param bool If true, $perm is treated as the "new default" |
|
1994 * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2. |
|
1995 */ |
|
1996 |
|
1997 function acl_merge_with_current($perm, $is_everyone = false, $scope = 2) |
|
1998 { |
|
1999 foreach ( $this->perms as $i => $p ) |
|
2000 { |
|
2001 if ( isset($perm[$i]) ) |
|
2002 { |
|
2003 if ( $is_everyone && !$this->acl_defaults_used[$i] ) |
|
2004 continue; |
|
2005 // Decide precedence |
|
2006 if ( isset($this->acl_defaults_used[$i]) ) |
|
2007 { |
|
2008 //echo "$i: default in use, overriding to: {$perm[$i]}<br />"; |
|
2009 // Defaults are in use, override |
|
2010 $this->perms[$i] = $perm[$i]; |
|
2011 $this->acl_defaults_used[$i] = ( $is_everyone ); |
|
2012 } |
|
2013 else |
|
2014 { |
|
2015 //echo "$i: default NOT in use"; |
|
2016 // Defaults are not in use, merge as normal |
|
2017 if ( $this->perms[$i] != AUTH_DENY ) |
|
2018 { |
|
2019 //echo ", but overriding"; |
|
2020 $this->perms[$i] = $perm[$i]; |
|
2021 } |
|
2022 //echo "<br />"; |
|
2023 } |
|
2024 } |
|
2025 } |
|
2026 } |
|
2027 |
|
2028 /** |
|
2029 * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence |
|
2030 * over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified |
|
2031 * defaults, which take precedence. |
|
2032 * @param array $perm1 The first set of permissions |
|
2033 * @param array $perm2 The second set of permissions |
|
2034 * @return array |
|
2035 */ |
|
2036 |
|
2037 function acl_merge_complete($perm1, $perm2) |
|
2038 { |
|
2039 $ret = $perm1; |
|
2040 foreach ( $perm2 as $type => $level ) |
|
2041 { |
|
2042 $ret[$type] = $level; |
|
2043 } |
|
2044 return $ret; |
|
2045 } |
|
2046 |
|
2047 /** |
|
2048 * Tell us if the dependencies for a given permission are met. |
|
2049 * @param string The ACL permission ID |
|
2050 * @return bool |
|
2051 */ |
|
2052 |
|
2053 function acl_check_deps($type) |
|
2054 { |
|
2055 if(!isset($this->acl_deps[$type])) // This will only happen if the permissions table is hacked or improperly accessed |
|
2056 return true; |
|
2057 if(sizeof($this->acl_deps[$type]) < 1) |
|
2058 return true; |
|
2059 $deps = $this->acl_deps[$type]; |
|
2060 while(true) |
|
2061 { |
|
2062 $full_resolved = true; |
|
2063 $j = sizeof($deps); |
|
2064 for ( $i = 0; $i < $j; $i++ ) |
|
2065 { |
|
2066 $b = $deps; |
|
2067 $deps = array_merge($deps, $this->acl_deps[$deps[$i]]); |
|
2068 if( $b == $deps ) |
|
2069 { |
|
2070 break 2; |
|
2071 } |
|
2072 $j = sizeof($deps); |
|
2073 } |
|
2074 } |
|
2075 //die('<pre>'.print_r($deps, true).'</pre>'); |
|
2076 foreach($deps as $d) |
|
2077 { |
|
2078 if ( !$this->get_permissions($d) ) |
|
2079 { |
|
2080 return false; |
|
2081 } |
|
2082 } |
|
2083 return true; |
|
2084 } |
|
2085 |
|
2086 /** |
|
2087 * Makes a CAPTCHA code and caches the code in the database |
|
2088 * @param int $len The length of the code, in bytes |
|
2089 * @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code. |
|
2090 */ |
|
2091 |
|
2092 function make_captcha($len = 7) |
|
2093 { |
|
2094 $chars = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9'); |
|
2095 $s = ''; |
|
2096 for($i=0;$i<$len;$i++) $s .= $chars[mt_rand(0, count($chars)-1)]; |
|
2097 $hash = md5(microtime() . mt_rand()); |
|
2098 $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key,salt,auth_level,source_ip,user_id) VALUES(\''.$hash.'\', \''.$s.'\', -1, \''.ip2hex($_SERVER['REMOTE_ADDR']).'\', -2);'); |
|
2099 return $hash; |
|
2100 } |
|
2101 |
|
2102 /** |
|
2103 * For the given code ID, returns the correct CAPTCHA code, or false on failure |
|
2104 * @param string $hash The unique ID assigned to the code |
|
2105 * @return string The correct confirmation code |
|
2106 */ |
|
2107 |
|
2108 function get_captcha($hash) |
|
2109 { |
|
2110 global $db, $session, $paths, $template, $plugins; // Common objects |
|
2111 $s = $this->sql('SELECT salt FROM '.table_prefix.'session_keys WHERE session_key=\''.$db->escape($hash).'\' AND source_ip=\''.ip2hex($_SERVER['REMOTE_ADDR']).'\';'); |
|
2112 if($db->numrows() < 1) return false; |
|
2113 $r = $db->fetchrow(); |
|
2114 return $r['salt']; |
|
2115 } |
|
2116 |
|
2117 /** |
|
2118 * Deletes all CAPTCHA codes cached in the DB for this user. |
|
2119 */ |
|
2120 |
|
2121 function kill_captcha() |
|
2122 { |
|
2123 $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE user_id=-2 AND source_ip=\''.ip2hex($_SERVER['REMOTE_ADDR']).'\';'); |
|
2124 } |
|
2125 |
|
2126 /** |
|
2127 * Generates a random password. |
|
2128 * @param int $length Optional - length of password |
|
2129 * @return string |
|
2130 */ |
|
2131 |
|
2132 function random_pass($length = 10) |
|
2133 { |
|
2134 $valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>'; |
|
2135 $valid_chars = enano_str_split($valid_chars); |
|
2136 $ret = ''; |
|
2137 for ( $i = 0; $i < $length; $i++ ) |
|
2138 { |
|
2139 $ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)]; |
|
2140 } |
|
2141 return $ret; |
|
2142 } |
|
2143 |
|
2144 /** |
|
2145 * Generates some Javascript that calls the AES encryption library. |
|
2146 * @param string The name of the form |
|
2147 * @param string The name of the password field |
|
2148 * @param string The name of the field that switches encryption on or off |
|
2149 * @param string The name of the field that contains the encryption key |
|
2150 * @param string The name of the field that will contain the encrypted password |
|
2151 * @param string The name of the field that handles MD5 challenge data |
|
2152 * @return string |
|
2153 */ |
|
2154 |
|
2155 function aes_javascript($form_name, $pw_field, $use_crypt, $crypt_key, $crypt_data, $challenge) |
|
2156 { |
|
2157 $code = ' |
|
2158 <script type="text/javascript"> |
|
2159 disableJSONExts(); |
|
2160 str = \'\'; |
|
2161 for(i=0;i<keySizeInBits/4;i++) str+=\'0\'; |
|
2162 var key = hexToByteArray(str); |
|
2163 var pt = hexToByteArray(str); |
|
2164 var ct = rijndaelEncrypt(pt, key, \'ECB\'); |
|
2165 var ct = byteArrayToHex(ct); |
|
2166 switch(keySizeInBits) |
|
2167 { |
|
2168 case 128: |
|
2169 v = \'66e94bd4ef8a2c3b884cfa59ca342b2e\'; |
|
2170 break; |
|
2171 case 192: |
|
2172 v = \'aae06992acbf52a3e8f4a96ec9300bd7aae06992acbf52a3e8f4a96ec9300bd7\'; |
|
2173 break; |
|
2174 case 256: |
|
2175 v = \'dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087\'; |
|
2176 break; |
|
2177 } |
|
2178 var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( ct == v && md5_vm_test() ); |
|
2179 var frm = document.forms.'.$form_name.'; |
|
2180 if(testpassed) |
|
2181 { |
|
2182 frm.'.$use_crypt.'.value = \'yes\'; |
|
2183 var cryptkey = frm.'.$crypt_key.'.value; |
|
2184 frm.'.$crypt_key.'.value = hex_md5(cryptkey); |
|
2185 cryptkey = hexToByteArray(cryptkey); |
|
2186 if(!cryptkey || ( ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ) && cryptkey.length != keySizeInBits / 8 ) |
|
2187 { |
|
2188 if ( frm._login ) frm._login.disabled = true; |
|
2189 len = ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ? \'\\nLen: \'+cryptkey.length : \'\'; |
|
2190 alert(\'The key is messed up\\nType: \'+typeof(cryptkey)+len); |
|
2191 } |
|
2192 } |
|
2193 if(frm.username) frm.username.focus(); |
|
2194 function runEncryption() |
|
2195 { |
|
2196 if(testpassed) |
|
2197 { |
|
2198 pass = frm.'.$pw_field.'.value; |
|
2199 chal = frm.'.$challenge.'.value; |
|
2200 challenge = hex_md5(pass + chal) + chal; |
|
2201 frm.'.$challenge.'.value = challenge; |
|
2202 pass = stringToByteArray(pass); |
|
2203 cryptstring = rijndaelEncrypt(pass, cryptkey, \'ECB\'); |
|
2204 if(!cryptstring) |
|
2205 { |
|
2206 return false; |
|
2207 } |
|
2208 cryptstring = byteArrayToHex(cryptstring); |
|
2209 frm.'.$crypt_data.'.value = cryptstring; |
|
2210 frm.'.$pw_field.'.value = \'\'; |
|
2211 } |
|
2212 return false; |
|
2213 } |
|
2214 </script> |
|
2215 '; |
|
2216 return $code; |
|
2217 } |
|
2218 |
|
2219 } |
|
2220 |
|
2221 /** |
|
2222 * Class used to fetch permissions for a specific page. Used internally by SessionManager. |
|
2223 * @package Enano |
|
2224 * @subpackage Session manager |
|
2225 * @license http://www.gnu.org/copyleft/gpl.html |
|
2226 * @access private |
|
2227 */ |
|
2228 |
|
2229 class Session_ACLPageInfo { |
|
2230 |
|
2231 /** |
|
2232 * The page ID of this ACL info package |
|
2233 * @var string |
|
2234 */ |
|
2235 |
|
2236 var $page_id; |
|
2237 |
|
2238 /** |
|
2239 * The namespace of the page being checked |
|
2240 * @var string |
|
2241 */ |
|
2242 |
|
2243 var $namespace; |
|
2244 |
|
2245 /** |
|
2246 * Our list of permission types. |
|
2247 * @access private |
|
2248 * @var array |
|
2249 */ |
|
2250 |
|
2251 var $acl_types = Array(); |
|
2252 |
|
2253 /** |
|
2254 * The list of descriptions for the permission types |
|
2255 * @var array |
|
2256 */ |
|
2257 |
|
2258 var $acl_descs = Array(); |
|
2259 |
|
2260 /** |
|
2261 * A list of dependencies for ACL types. |
|
2262 * @var array |
|
2263 */ |
|
2264 |
|
2265 var $acl_deps = Array(); |
|
2266 |
|
2267 /** |
|
2268 * Our tell-all list of permissions. |
|
2269 * @access private - or, preferably, protected...too bad this has to be PHP4 compatible |
|
2270 * @var array |
|
2271 */ |
|
2272 |
|
2273 var $perms = Array(); |
|
2274 |
|
2275 /** |
|
2276 * Constructor. |
|
2277 * @param string $page_id The ID of the page to check |
|
2278 * @param string $namespace The namespace of the page to check. |
|
2279 * @param array $acl_types List of ACL types |
|
2280 * @param array $acl_descs List of human-readable descriptions for permissions (associative) |
|
2281 * @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page. |
|
2282 * @param array $base What to start with - this is an attempt to reduce the number of SQL queries. |
|
2283 */ |
|
2284 |
|
2285 function Session_ACLPageInfo($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base) |
|
2286 { |
|
2287 global $db, $session, $paths, $template, $plugins; // Common objects |
|
2288 |
|
2289 $this->perms = $session->acl_merge_complete($acl_types, $base); |
|
2290 $this->acl_deps = $acl_deps; |
|
2291 $this->acl_types = $acl_types; |
|
2292 $this->acl_descs = $acl_descs; |
|
2293 |
|
2294 // Build a query to grab ACL info |
|
2295 $bs = 'SELECT rules FROM '.table_prefix.'acl WHERE ( '; |
|
2296 $q = Array(); |
|
2297 $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$session->user_id.' )'; |
|
2298 if(count($session->groups) > 0) |
|
2299 { |
|
2300 foreach($session->groups as $g_id => $g_name) |
|
2301 { |
|
2302 $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )'; |
|
2303 } |
|
2304 } |
|
2305 // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual |
|
2306 // permissions to override group permissions. |
|
2307 $bs .= implode(' OR ', $q) . ' ) AND ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) |
|
2308 ORDER BY target_type ASC, page_id ASC, namespace ASC;'; |
|
2309 $q = $session->sql($bs); |
|
2310 if ( $row = $db->fetchrow() ) |
|
2311 { |
|
2312 do { |
|
2313 $rules = $session->string_to_perm($row['rules']); |
|
2314 $this->perms = $session->acl_merge($this->perms, $rules); |
|
2315 } while ( $row = $db->fetchrow() ); |
|
2316 } |
|
2317 |
|
2318 $this->page_id = $page_id; |
|
2319 $this->namespace = $namespace; |
|
2320 } |
|
2321 |
|
2322 /** |
|
2323 * Tells us whether permission $type is allowed or not based on the current rules. |
|
2324 * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type()) |
|
2325 * @param bool $no_deps If true, disables dependency checking |
|
2326 * @return bool True if allowed, false if denied or if an error occured |
|
2327 */ |
|
2328 |
|
2329 function get_permissions($type, $no_deps = false) |
|
2330 { |
|
2331 global $db, $session, $paths, $template, $plugins; // Common objects |
|
2332 if ( isset( $this->perms[$type] ) ) |
|
2333 { |
|
2334 if ( $this->perms[$type] == AUTH_DENY ) |
|
2335 $ret = false; |
|
2336 else if ( $this->perms[$type] == AUTH_WIKIMODE && |
|
2337 ( isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) && |
|
2338 ( $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '1' || |
|
2339 ( $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '2' |
|
2340 && getConfig('wiki_mode') == '1' |
|
2341 ) ) ) ) |
|
2342 $ret = true; |
|
2343 else if ( $this->perms[$type] == AUTH_WIKIMODE && ( |
|
2344 !isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) |
|
2345 || ( |
|
2346 isset($paths->pages[$paths->nslist[$this->namespace].$this->page_id]) && ( |
|
2347 $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '0' |
|
2348 || ( |
|
2349 $paths->pages[$paths->nslist[$this->namespace].$this->page_id]['wiki_mode'] == '2' && getConfig('wiki_mode') != '1' |
|
2350 ) ) ) ) ) |
|
2351 $ret = false; |
|
2352 else if ( $this->perms[$type] == AUTH_ALLOW ) |
|
2353 $ret = true; |
|
2354 else if ( $this->perms[$type] == AUTH_DISALLOW ) |
|
2355 $ret = false; |
|
2356 } |
|
2357 else if(isset($this->acl_types[$type])) |
|
2358 { |
|
2359 if ( $this->acl_types[$type] == AUTH_DENY ) |
|
2360 $ret = false; |
|
2361 else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode ) |
|
2362 $ret = true; |
|
2363 else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode ) |
|
2364 $ret = false; |
|
2365 else if ( $this->acl_types[$type] == AUTH_ALLOW ) |
|
2366 $ret = true; |
|
2367 else if ( $this->acl_types[$type] == AUTH_DISALLOW ) |
|
2368 $ret = false; |
|
2369 } |
|
2370 else |
|
2371 { |
|
2372 // ACL type is undefined |
|
2373 trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING); |
|
2374 return false; // Be on the safe side and deny access |
|
2375 } |
|
2376 if ( !$no_deps ) |
|
2377 { |
|
2378 if ( !$this->acl_check_deps($type) ) |
|
2379 return false; |
|
2380 } |
|
2381 return $ret; |
|
2382 } |
|
2383 |
|
2384 /** |
|
2385 * Tell us if the dependencies for a given permission are met. |
|
2386 * @param string The ACL permission ID |
|
2387 * @return bool |
|
2388 */ |
|
2389 |
|
2390 function acl_check_deps($type) |
|
2391 { |
|
2392 if(!isset($this->acl_deps[$type])) // This will only happen if the permissions table is hacked or improperly accessed |
|
2393 return true; |
|
2394 if(sizeof($this->acl_deps[$type]) < 1) |
|
2395 return true; |
|
2396 $deps = $this->acl_deps[$type]; |
|
2397 while(true) |
|
2398 { |
|
2399 $full_resolved = true; |
|
2400 $j = sizeof($deps); |
|
2401 for ( $i = 0; $i < $j; $i++ ) |
|
2402 { |
|
2403 $b = $deps; |
|
2404 $deps = array_merge($deps, $this->acl_deps[$deps[$i]]); |
|
2405 if( $b == $deps ) |
|
2406 { |
|
2407 break 2; |
|
2408 } |
|
2409 $j = sizeof($deps); |
|
2410 } |
|
2411 } |
|
2412 //die('<pre>'.print_r($deps, true).'</pre>'); |
|
2413 foreach($deps as $d) |
|
2414 { |
|
2415 if ( !$this->get_permissions($d) ) |
|
2416 { |
|
2417 return false; |
|
2418 } |
|
2419 } |
|
2420 return true; |
|
2421 } |
|
2422 |
|
2423 } |
|
2424 |
|
2425 ?> |