includes/sessions.php
changeset 270 5bcdee999015
parent 268 58477ab3937f
child 271 f088805540ae
child 286 b2f985e4cef3
equal deleted inserted replaced
269:06db76725891 270:5bcdee999015
   149    * Regex that defines a valid username, minus the ^ and $, these are added later
   149    * Regex that defines a valid username, minus the ^ and $, these are added later
   150    * @var string
   150    * @var string
   151    */
   151    */
   152    
   152    
   153   //var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)';
   153   //var $valid_username = '([A-Za-z0-9 \!\@\(\)-]+)';
   154   var $valid_username = '([^<>_&\?\'"%\n\r\t\a\/]+)';
   154   var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
   155    
   155    
   156   /**
   156   /**
   157    * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
   157    * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
   158    * @var string
   158    * @var string
   159    */
   159    */
   557    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   557    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   558    * @param int $level The privilege level we're authenticating for, defaults to 0
   558    * @param int $level The privilege level we're authenticating for, defaults to 0
   559    * @return string 'success' on success, or error string on failure
   559    * @return string 'success' on success, or error string on failure
   560    */
   560    */
   561    
   561    
   562   function login_with_crypto($username, $aes_data, $aes_key, $challenge, $level = USER_LEVEL_MEMBER)
   562   function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER)
   563   {
   563   {
   564     global $db, $session, $paths, $template, $plugins; // Common objects
   564     global $db, $session, $paths, $template, $plugins; // Common objects
   565     
   565     
   566     $privcache = $this->private_key;
   566     $privcache = $this->private_key;
   567     
   567     
   568     // Instanciate the Rijndael encryption object
   568     // Instanciate the Rijndael encryption object
   569     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   569     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   570     
   570     
   571     // Fetch our decryption key
   571     // Fetch our decryption key
   572     
   572     
   573     $aes_key = $this->fetch_public_key($aes_key);
   573     $aes_key = $this->fetch_public_key($aes_key_id);
   574     if(!$aes_key)
   574     if(!$aes_key)
   575       return 'Couldn\'t look up public key "'.$aes_key.'" for decryption';
   575       return 'Couldn\'t look up public key "'.htmlspecialchars($aes_key_id).'" for decryption';
   576     
   576     
   577     // Convert the key to a binary string
   577     // Convert the key to a binary string
   578     $bin_key = hexdecode($aes_key);
   578     $bin_key = hexdecode($aes_key);
   579     
   579     
   580     if(strlen($bin_key) != AES_BITS / 8)
   580     if(strlen($bin_key) != AES_BITS / 8)
   585     
   585     
   586     // Initialize our success switch
   586     // Initialize our success switch
   587     $success = false;
   587     $success = false;
   588     
   588     
   589     // Escaped username
   589     // Escaped username
       
   590     $username = str_replace('_', ' ', $username);
   590     $db_username_lower = $this->prepare_text(strtolower($username));
   591     $db_username_lower = $this->prepare_text(strtolower($username));
   591     $db_username       = $this->prepare_text($username);
   592     $db_username       = $this->prepare_text($username);
   592     
   593     
   593     // Select the user data from the table, and decrypt that so we can verify the password
   594     // Select the user data from the table, and decrypt that so we can verify the password
   594     $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$db_username_lower.'\' OR username=\'' . $db_username . '\';');
   595     $this->sql('SELECT password,old_encryption,user_id,user_level,theme,style,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$db_username_lower.'\' OR username=\'' . $db_username . '\';');
   700   {
   701   {
   701     global $db, $session, $paths, $template, $plugins; // Common objects
   702     global $db, $session, $paths, $template, $plugins; // Common objects
   702     
   703     
   703     $pass_hashed = ( $already_md5ed ) ? $password : md5($password);
   704     $pass_hashed = ( $already_md5ed ) ? $password : md5($password);
   704     
   705     
       
   706     // Replace underscores with spaces in username
       
   707     // (Added in 1.0.2)
       
   708     $username = str_replace('_', ' ', $username);
       
   709     
   705     // Perhaps we're upgrading Enano?
   710     // Perhaps we're upgrading Enano?
   706     if($this->compat)
   711     if($this->compat)
   707     {
   712     {
   708       return $this->login_compat($username, $pass_hashed, $level);
   713       return $this->login_compat($username, $pass_hashed, $level);
   709     }
   714     }
   835     }
   840     }
   836   }
   841   }
   837   
   842   
   838   /**
   843   /**
   839    * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated!
   844    * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated!
   840    * Basically the session key is a base64-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password]"
   845    * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
   841    * @param int $user_id
   846    * @param int $user_id
   842    * @param string $username
   847    * @param string $username
   843    * @param string $password
   848    * @param string $password
   844    * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
   849    * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
   845    * @return bool
   850    * @return bool
   894     $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.');');
   899     $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.');');
   895     return true;
   900     return true;
   896   }
   901   }
   897   
   902   
   898   /**
   903   /**
   899    * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this.
   904    * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
   900    * @see sessionManager::register_session()
   905    * @see sessionManager::register_session()
   901    * @access private
   906    * @access private
   902    */
   907    */
   903   
   908   
   904   function register_session_compat($user_id, $username, $password, $level = 0)
   909   function register_session_compat($user_id, $username, $password, $level = 0)
  1336    */
  1341    */
  1337    
  1342    
  1338   function check_banlist()
  1343   function check_banlist()
  1339   {
  1344   {
  1340     global $db, $session, $paths, $template, $plugins; // Common objects
  1345     global $db, $session, $paths, $template, $plugins; // Common objects
  1341     if($this->compat)
  1346     $col_reason = ( $this->compat ) ? '"No reason entered (session manager is in compatibility mode)" AS reason' : 'reason';
  1342       $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;');
  1347     $is_banned = false;
  1343     else
  1348     if ( $this->user_logged_in )
  1344       $q = $this->sql('SELECT ban_id,ban_type,ban_value,is_regex,reason FROM '.table_prefix.'banlist ORDER BY ban_type;');
  1349     {
  1345     if(!$q) $db->_die('The banlist data could not be selected.');
  1350       // check by IP, email, and username
  1346     $banned = false;
  1351       $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1347     while($row = $db->fetchrow())
  1352             . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1348     {
  1353             . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value ) OR \n"
  1349       if($this->compat)
  1354             . "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
  1350         $row['reason'] = 'None available - session manager is in compatibility mode';
  1355             . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' REGEXP ban_value ) OR \n"
  1351       switch($row['ban_type'])
  1356             . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
  1352       {
  1357             . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' REGEXP ban_value ) \n"
  1353       case BAN_IP:
  1358             . "  ORDER BY ban_type ASC;";
  1354         if(intval($row['is_regex'])==1) {
  1359       $q = $this->sql($sql);
  1355           if(preg_match('#'.$row['ban_value'].'#i', $_SERVER['REMOTE_ADDR']))
  1360       if ( $db->numrows() > 0 )
       
  1361       {
       
  1362         while ( list($reason, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
       
  1363         {
       
  1364           if ( $ban_type == BAN_IP && $row['is_regex'] != 1 )
  1356           {
  1365           {
       
  1366             // check range
       
  1367             $regexp = parse_ip_range_regex($ban_value);
       
  1368             if ( !$regexp )
       
  1369             {
       
  1370               continue;
       
  1371             }
       
  1372             if ( preg_match("/$regexp/", $_SERVER['REMOTE_ADDR']) )
       
  1373             {
       
  1374               $banned = true;
       
  1375             }
       
  1376           }
       
  1377           else
       
  1378           {
       
  1379             // User is banned
  1357             $banned = true;
  1380             $banned = true;
  1358             $reason = $row['reason'];
       
  1359           }
  1381           }
  1360         }
  1382         }
  1361         else {
  1383       }
  1362           if($row['ban_value']==$_SERVER['REMOTE_ADDR']) { $banned = true; $reason = $row['reason']; }
  1384       $db->free_result();
  1363         }
  1385     }
  1364         break;
  1386     else
  1365       case BAN_USER:
  1387     {
  1366         if(intval($row['is_regex'])==1) {
  1388       // check by IP only
  1367           if(preg_match('#'.$row['ban_value'].'#i', $this->username))
  1389       $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
       
  1390                 ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
       
  1391                 ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value )
       
  1392               ORDER BY ban_type ASC;";
       
  1393       $q = $this->sql($sql);
       
  1394       if ( $db->numrows() > 0 )
       
  1395       {
       
  1396         while ( list($reason, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
       
  1397         {
       
  1398           if ( $ban_type == BAN_IP && $row['is_regex'] != 1 )
  1368           {
  1399           {
       
  1400             // check range
       
  1401             $regexp = parse_ip_range_regex($ban_value);
       
  1402             if ( !$regexp )
       
  1403               continue;
       
  1404             if ( preg_match("/$regexp/", $_SERVER['REMOTE_ADDR']) )
       
  1405             {
       
  1406               $banned = true;
       
  1407             }
       
  1408           }
       
  1409           else
       
  1410           {
       
  1411             // User is banned
  1369             $banned = true;
  1412             $banned = true;
  1370             $reason = $row['reason'];
       
  1371           }
  1413           }
  1372         }
  1414         }
  1373         else {
  1415       }
  1374           if($row['ban_value']==$this->username) { $banned = true; $reason = $row['reason']; }
  1416       $db->free_result();
  1375         }
  1417     }
  1376         break;
  1418     if ( $banned && $paths->get_pageid_from_url() != $paths->nslist['Special'].'CSS' )
  1377       case BAN_EMAIL:
       
  1378         if(intval($row['is_regex'])==1) {
       
  1379           if(preg_match('#'.$row['ban_value'].'#i', $this->email))
       
  1380           {
       
  1381             $banned = true;
       
  1382             $reason = $row['reason'];
       
  1383           }
       
  1384         }
       
  1385         else {
       
  1386           if($row['ban_value']==$this->email) { $banned = true; $reason = $row['reason']; }
       
  1387         }
       
  1388         break;
       
  1389       default:
       
  1390         die('Ban error: rule "'.$row['ban_value'].'" has an invalid type ('.$row['ban_type'].')');
       
  1391       }
       
  1392     }
       
  1393     if($banned && $paths->get_pageid_from_url() != $paths->nslist['Special'].'CSS')
       
  1394     {
  1419     {
  1395       // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
  1420       // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
  1396       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>');
  1421       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>');
  1397       exit;
  1422       exit;
  1398     }
  1423     }
  1400   
  1425   
  1401   # Registration
  1426   # Registration
  1402   
  1427   
  1403   /**
  1428   /**
  1404    * Registers a user. This does not perform any type of login.
  1429    * Registers a user. This does not perform any type of login.
  1405    * @param string $username
  1430    * @param string New user's username
  1406    * @param string $password This should be unencrypted.
  1431    * @param string This should be unencrypted.
  1407    * @param string $email
  1432    * @param string E-mail address.
  1408    * @param string $real_name Optional, defaults to ''.
  1433    * @param string Optional, defaults to ''.
  1409    * @param bool   $coppa     Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
  1434    * @param bool Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
  1410    */
  1435    */
  1411    
  1436    
  1412   function create_user($username, $password, $email, $real_name = '', $coppa = false)
  1437   function create_user($username, $password, $email, $real_name = '', $coppa = false)
  1413   {
  1438   {
  1414     global $db, $session, $paths, $template, $plugins; // Common objects
  1439     global $db, $session, $paths, $template, $plugins; // Common objects
  1415     
  1440     
  1416     // Initialize AES
  1441     // Initialize AES
  1417     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1442     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1418     
  1443     
  1419     if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.';
  1444     if(!preg_match('#^'.$this->valid_username.'$#', $username)) return 'The username you chose contains invalid characters.';
       
  1445     $username = str_replace('_', ' ', $username);
  1420     $user_orig = $username;
  1446     $user_orig = $username;
  1421     $username = $this->prepare_text($username);
  1447     $username = $this->prepare_text($username);
  1422     $email = $this->prepare_text($email);
  1448     $email = $this->prepare_text($email);
  1423     $real_name = $this->prepare_text($real_name);
  1449     $real_name = $this->prepare_text($real_name);
  1424     
  1450