false, 'flags' => false, 'allow' => false, 'deny' => false, 'guest' => true, ); $levelorder = array( 1 => 'janitor', 10 => 'mod', 20 => 'manager', 50 => 'admin' ); $levelorderf = array( 'janitor' => 1, 'mod' => 10, 'manager' => 20, 'admin' => 50 ); if (!defined('SQLLOGMOD')) { define("SQLLOGMOD", "mod_users"); define('PASS_TIMEOUT', 1800); define('LOGIN_FAIL_HOURLY', 5); } function csrf_tag() { if (isset($_COOKIE['_tkn'])) { return ''; } else { return ''; } } function csrf_attr() { if (isset($_COOKIE['_tkn'])) { return 'data-tkn="' . htmlspecialchars($_COOKIE['_tkn'], ENT_QUOTES) . '"'; } else { return ''; } } function auth_encrypt($data) { $key = file_get_contents('/www/keys/2015_enc.key'); if (!$key) { return false; } $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv); if ($encrypted === false) { return false; } return $iv . $encrypted; } function auth_decrypt($data) { $key = file_get_contents('/www/keys/2015_enc.key'); if (!$key) { return false; } $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); $iv_dec = substr($data, 0, $iv_size); $data = substr($data, $iv_size); $data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv_dec); if ($data === false) { return false; } return rtrim($data, "\0"); } function verify_one_time_pwd($username, $otp) { if (!$otp) { return false; } $query = "SELECT auth_secret FROM mod_users WHERE username = '%s' LIMIT 1"; $res = mysql_global_call($query, $username); if (!$res) { return false; } $enc_secret = mysql_fetch_row($res)[0]; if (!$enc_secret) { return false; } require_once 'lib/GoogleAuthenticator.php'; $ga = new PHPGangsta_GoogleAuthenticator(); $dec_secret = auth_decrypt($enc_secret); if ($dec_secret === false) { return false; } if ($ga->verifyCode($dec_secret, $otp, 2)) { return true; } return false; } /** * Returns a hash containing implicit levels for the current authed level * ex: will return array('janitor' => true, 'mod' => true) * if the current level is 'mod' */ function get_level_map($level = null) { global $auth, $levelorderf; $map = array(); if (!$level) { $level = $auth['level']; } if (!$level) { return $map; } $level_value = (int)$levelorderf[$level]; foreach ($levelorderf as $k => $v) { if ($v <= $level_value) { $map[$k] = true; } } return $map; } function has_level( $level = 'mod', $board = false ) { if( is_local_auth() ) return YES; global $auth, $levelorder, $levelorderf; static $ourlevel = -1; //if( !$board && defined( 'BOARD_DIR' ) ) $board = BOARD_DIR; //if( !access_board($board) ) return false; if( $ourlevel < 0 ) $ourlevel = $levelorderf[$auth['level']]; if (!isset($levelorderf[$level])) { return false; } if( $levelorderf[$level] <= $ourlevel ) return true; return false; } function has_flag( $flag, $board = false ) { if( is_local_auth() ) return YES; global $auth; if( $auth['guest'] ) return false; if( !access_board( $board ) ) return false; if( in_array( $flag, $auth['flags'] ) ) return true; return false; } function access_board( $board ) { if( is_local_auth() ) return YES; global $auth; if( $auth['guest'] ) return false; $can_do = false; // See if we have access to this board or all if( in_array( 'all', $auth['allow'] ) || in_array( $board, $auth['allow'] ) ) $can_do = true; // Are we denied on this board? if( $board && in_array( $board, $auth['deny'] ) ) $can_do = false; // If we're not using a board, are we denied for no-board stuff? if( !$board && in_array( 'noboard', $auth['deny'] ) ) $can_do = false; return $can_do; } function is_user() { if( is_local_auth() ) return YES; global $auth; if( $auth['guest'] ) return false; if( $auth['level'] ) return true; return false; } function auth_user($skip_agreement = false) { global $auth; $user = $_COOKIE['4chan_auser']; $pass = $_COOKIE['apass']; if( !$user || !$pass ) return false; $query = mysql_global_call("SELECT * FROM `%s` WHERE `username` = '%s' LIMIT 1", SQLLOGMOD, $user); if (!mysql_num_rows($query)) { return false; } $fetch = mysql_fetch_assoc($query); $admin_salt = file_get_contents('/www/keys/2014_admin.salt'); if (!$admin_salt) { die('Internal Server Error (s0)'); } $hashed_admin_password = hash('sha256', $fetch['username'] . $fetch['password'] . $admin_salt); if ($hashed_admin_password !== $pass) { return false; } if ($fetch['password_expired'] == 1) { die('Your password has expired; check IRC for instructions on changing it.'); } if (!$skip_agreement) { if ($fetch['signed_agreement'] == 0 && basename($_SERVER['SELF_PATH']) !== 'agreement.php' && basename($_SERVER['SELF_PATH']) !== 'agreement_genkey.php') { die('You must agree to the 4chan Volunteer Moderator Agreement in order to access moderation tools. Please check your e-mail for more information.'); } } $auth['level'] = $fetch['level']; $auth['flags'] = explode( ',', $fetch['flags'] ); $auth['allow'] = explode( ',', $fetch['allow'] ); $auth['deny'] = explode( ',', $fetch['deny'] ); $auth['guest'] = false; $flags = array(); if( has_level( 'admin' ) ) { $flags['forcedanonname'] = 2; } if( has_level( 'manager' ) || has_flag( 'html' ) ) { $flags['html'] = 1; } $flags = array_flip( $flags ); $flags = implode( ',', $flags ); $ips_array = json_decode($fetch['ips'], true); if (json_last_error() !== JSON_ERROR_NONE) { die('Database Error (1-0)'); } $ips_array[$_SERVER['REMOTE_ADDR']] = $_SERVER['REQUEST_TIME']; if (count($ips_array) > 512) { asort($ips_array); array_shift($ips_array); } $ips_array = json_encode($ips_array); if (json_last_error() !== JSON_ERROR_NONE) { die('Database Error (1-1)'); } if (mb_strlen($_SERVER['HTTP_USER_AGENT']) > 128) { $ua = mb_substr($_SERVER['HTTP_USER_AGENT'], 0, 128); } else { $ua = $_SERVER['HTTP_USER_AGENT']; } mysql_global_call("UPDATE `%s` SET ips = '$ips_array', last_ua = '%s' WHERE id = %d LIMIT 1", SQLLOGMOD, $ua, $fetch['id']); return true; } // OLD auth /* function auth_user( $login = false ) { global $auth; if( $login ) { $user = $_POST['userlogin']; $pass = $_POST['passlogin']; } else { $user = $_COOKIE['4chan_auser']; $pass = $_COOKIE['4chan_apass']; } if( !$user || !$pass ) return false; $query = mysql_global_call( "SELECT * FROM `%s` WHERE `username` = '%s' LIMIT 1", SQLLOGMOD, $user ); if( !mysql_num_rows( $query ) ) return false; $fetch = mysql_fetch_assoc( $query ); if( $fetch['password_expired'] == 1 ) { die( 'Your password has expired; check IRC for instructions on changing it.' ); } if ($login) { if( !password_verify($pass, $fetch['password'])) return false; $pass = $fetch['password']; } else { if ($pass != $fetch['password']) return false; } $auth['level'] = $fetch['level']; $auth['flags'] = explode( ',', $fetch['flags'] ); $auth['allow'] = explode( ',', $fetch['allow'] ); $auth['deny'] = explode( ',', $fetch['deny'] ); $auth['guest'] = false; $flags = array(); if( has_level( 'admin' ) && $user == 'moot' ) { $flags['forcedanonname'] = 2; } if( has_level( 'manager' ) || has_flag( 'html' ) ) { $flags['html'] = 1; } $flags = array_flip( $flags ); $flags = implode( ',', $flags ); $ips_array = json_decode($fetch['ips'], true); if (json_last_error() !== JSON_ERROR_NONE) { die('Database Error (1-0)'); } $ips_array[$_SERVER['REMOTE_ADDR']] = $_SERVER['REQUEST_TIME']; $ips_array = json_encode($ips_array); if (json_last_error() !== JSON_ERROR_NONE) { die('Database Error (1-1)'); } if ($login) { $login_query = ", last_login = now()"; } else { if (!isset($_COOKIE['apass'])) { return false; } $admin_salt = file_get_contents('/www/keys/2014_admin.salt'); if (!$admin_salt) { die('Internal Server Error (s0)'); } $hashed_admin_cookie = $_COOKIE['apass']; $hashed_admin_password = hash('sha256', $fetch['username'] . $fetch['password'] . $admin_salt); if ($hashed_admin_password !== $hashed_admin_cookie) { return false; } $login_query = ''; } mysql_global_do("UPDATE `%s` SET ips = '$ips_array' $login_query WHERE id = %d", SQLLOGMOD, $fetch['id']); if( !isset( $_COOKIE['4chan_auser'] ) || !isset( $_COOKIE['4chan_apass'] ) ) { if( strstr( $_SERVER["HTTP_HOST"], ".4chan.org" ) ) { setcookie( "4chan_auser", $user, time() + 30 * 24 * 3600, "/", ".4chan.org", true, true ); setcookie( "4chan_apass", $pass, time() + 30 * 24 * 3600, "/", ".4chan.org", true, true ); setcookie( "4chan_aflags", $flags, time() + 30 * 24 * 3600, "/", ".4chan.org", true ); $jspath = $auth['level'] == 'janitor' ? JANITOR_JS_PATH : ADMIN_JS_PATH; if( !isset( $_COOKIE['extra_path'] ) || !in_array( $_COOKIE['extra_path'], array(JANITOR_JS_PATH, ADMIN_JS_PATH) ) ) { setcookie( 'extra_path', $jspath, time() + ( 30 * 24 * 3600 ), '/', '.4chan.org' ); } } elseif( strstr( $_SERVER["HTTP_HOST"], ".4channel.org" ) ) { setcookie( "4chan_auser", $user, time() + 30 * 24 * 3600, "/", ".4channel.org", true, true ); setcookie( "4chan_apass", $pass, time() + 30 * 24 * 3600, "/", ".4channel.org", true, true ); } else { die( 'Not 4chan.org' ); } } return true; } */ function is_local_auth() { if (!isset($_SERVER['REMOTE_ADDR'])) { return true; } // local rpc can do anything $longip = ip2long( $_SERVER['REMOTE_ADDR'] ); if( cidrtest( $longip, "10.0.0.0/24" ) || cidrtest( $longip, "204.152.204.0/24" ) || cidrtest( $longip, "127.0.0.0/24" ) ) { return YES; } return false; } function can_delete( $resno ) { if( !has_level( 'janitor' ) ) return false; if( has_level( 'janitor' ) && access_board( BOARD_DIR ) ) return true; //if( !access_board(BOARD_DIR) ) return false; $query = mysql_global_do( "SELECT COUNT(*) from reports WHERE board='%s' AND no=%d AND cat=2", BOARD_DIR, $resno ); $illegal_count = mysql_result( $query, 0, 0 ); mysql_free_result( $query ); return $illegal_count >= 3; } function start_auth_captcha($use_alt_captcha = false) { if (valid_captcha_bypass() !== true) { if ($use_alt_captcha) { start_recaptcha_verify_alt(); } else { start_recaptcha_verify(); } } } function clear_pass_cookies() { setcookie('pass_id', null, 1, '/', 'sys.4chan.org', true, true); setcookie('pass_id', null, 1, '/', '.4chan.org', true, true); setcookie('pass_enabled', null, 1, '/', '.4chan.org'); } function valid_captcha_bypass() { global $captcha_bypass, $passid, $rangeban_bypass; $captcha_bypass = false; $rangeban_bypass = false; $passid = ''; if (is_local_auth() || has_level('janitor')) { $captcha_bypass = true; $rangeban_bypass = true; return true; } if (CAPTCHA != 1) { $captcha_bypass = true; } $time = $_SERVER['REQUEST_TIME']; $host = $_SERVER['REMOTE_ADDR']; // check for 4chan pass $pass_cookie = isset( $_COOKIE['pass_id'] ) ? $_COOKIE['pass_id'] : ''; if (strlen($pass_cookie) == 10) { setcookie('pass_id', '0', 1, '/', '.4chan.org', true, true); setcookie('pass_enabled', '0', 1, '/', '.4chan.org'); error(S_PASSFORMATCHANGED); } if ($pass_cookie) { $pass_parts = explode('.', $pass_cookie); $pass_user = $pass_parts[0]; $pass_session = $pass_parts[1]; if (!$pass_user || !$pass_session) { error(S_INVALIDPASS); } // The column is case insensitive but all passes should be uppercase to avoid ban bypassing exploits. $pass_user = strtoupper($pass_user); $admin_salt = file_get_contents('/www/keys/2014_admin.salt'); if (!$admin_salt) { die('Internal Server Error (s0)'); } $passq = mysql_global_call("SELECT user_hash, session_id, last_ip, last_used, last_country, status, pending_id, UNIX_TIMESTAMP(expiration_date) as expiration_date FROM pass_users WHERE pin != '' AND user_hash = '%s'", $pass_user); if( !$passq ) error( S_INVALIDPASS ); $res = mysql_fetch_assoc($passq); if (!$res || !$res['session_id']) { clear_pass_cookies(); error(S_INVALIDPASS); } $hashed_pass_session = substr(hash('sha256', $res['session_id'] . $admin_salt), 0, 32); if ($hashed_pass_session !== $pass_session) { clear_pass_cookies(); error(S_INVALIDPASS); } if ((int)$res['expiration_date'] <= $time) { clear_pass_cookies(); error(sprintf(S_PASSEXPIRED, $res['pending_id'])); } if ($res['status'] != 0) { clear_pass_cookies(); error(S_PASSDISABLED); } $lastused = strtotime( $res['last_used'] ); $lastip_mask = ip2long( $res['last_ip'] ) & ( ~255 ); $ip_mask = ip2long( $host ) & ( ~255 ); if( $lastip_mask !== 0 && ( $time - $lastused ) < PASS_TIMEOUT && $lastip_mask != $ip_mask ) { // old strict code, above is to match last octet //if( ( $time - $lastused ) < PASS_TIMEOUT && $res['last_ip'] != $host && $res['last_ip'] != '0.0.0.0' ) { clear_pass_cookies(); error( S_PASSINUSE ); } $update_country = ''; if ($res['last_ip'] !== $host) { $geo_data = GeoIP2::get_country($host); if ($geo_data && isset($geo_data['country_code'])) { $country_code = $geo_data['country_code']; } else { $country_code = 'XX'; } $update_country = ", last_country = '" . mysql_real_escape_string($country_code) . "'"; } $passid = $pass_user; $captcha_bypass = true; $rangeban_bypass = true; mysql_global_call( "UPDATE pass_users SET last_used = NOW(), last_ip = '%s' $update_country WHERE user_hash = '%s' AND status = 0 LIMIT 1", $host, $res['user_hash'], $host ); } return $captcha_bypass; } // some code paths might think current admin name is 4chan_auser cookie // when that's not set (e.g. local requests), assert out here function validate_admin_cookies() { if (!$_COOKIE['4chan_auser']) { error('Internal error (internal request missing name)'); } }