# HG changeset patch # User Dan # Date 1188444906 14400 # Node ID 93ef7df77847fad0daa8d580008626c5f1de808a # Parent 1c7f59df94742bf9318761c6337126ad9e90fed8 Added a ton of new log points for administrator actions; restructured security log view and enabled pagination for security logs; string change in ajax.php for RDNS operation failure diff -r 1c7f59df9474 -r 93ef7df77847 ajax.php --- a/ajax.php Wed Aug 29 18:23:37 2007 -0400 +++ b/ajax.php Wed Aug 29 23:35:06 2007 -0400 @@ -188,7 +188,7 @@ if(!$session->get_permissions('mod_misc')) die('Go somewhere else for your reverse DNS info!'); $ip = $_GET['ip']; $rdns = gethostbyaddr($ip); - if($rdns == $ip) echo 'Unable to get reverse DNS information. Perhaps the IP address does not exist anymore.'; + if($rdns == $ip) echo 'Unable to get reverse DNS information. Perhaps the DNS server is down or the PTR record no longer exists.'; else echo $rdns; break; case 'acljson': diff -r 1c7f59df9474 -r 93ef7df77847 includes/paths.php --- a/includes/paths.php Wed Aug 29 18:23:37 2007 -0400 +++ b/includes/paths.php Wed Aug 29 23:35:06 2007 -0400 @@ -89,8 +89,9 @@ $this->addAdminNode('Users', 'Manage users', 'UserManager'); $this->addAdminNode('Users', 'Edit groups', 'GroupManager'); $this->addAdminNode('Users', 'COPPA support', 'COPPA'); - $this->addAdminNode('Users', 'Ban control', 'BanControl'); $this->addAdminNode('Users', 'Mass e-mail', 'MassEmail'); + $this->addAdminNode('Security', 'Security log', 'SecurityLog'); + $this->addAdminNode('Security', 'Ban control', 'BanControl'); $code = $plugins->setHook('acl_rule_init'); foreach ( $code as $cmd ) diff -r 1c7f59df9474 -r 93ef7df77847 plugins/SpecialAdmin.php --- a/plugins/SpecialAdmin.php Wed Aug 29 18:23:37 2007 -0400 +++ b/plugins/SpecialAdmin.php Wed Aug 29 23:35:06 2007 -0400 @@ -41,6 +41,7 @@ // Admin pages that were too enormous to be in this file were split off into the plugins/admin/ directory in 1.0.1 require(ENANO_ROOT . '/plugins/admin/PageGroups.php'); +require(ENANO_ROOT . '/plugins/admin/SecurityLog.php'); // function names are IMPORTANT!!! The name pattern is: page__ @@ -104,61 +105,10 @@ // Security log echo '

Security log

'; - echo '
'; - $cls = 'row2'; - echo ''; - require('config.php'); - $hash = md5($dbpasswd); - unset($dbname, $dbhost, $dbuser, $dbpasswd); - unset($dbname, $dbhost, $dbuser, $dbpasswd); // PHP5 Zend bug - if ( defined('ENANO_DEMO_MODE') && !isset($_GET[ $hash ]) && substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' ) - { - echo ''; - } - else - { - if(isset($_GET['fulllog'])) - { - $l = 'SELECT action,date_string,author,edit_summary,time_id,page_text FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC;'; - } - else - { - $l = 'SELECT action,date_string,author,edit_summary,time_id,page_text FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC LIMIT 5'; - } - $q = $db->sql_query($l); - while($r = $db->fetchrow()) - { - if ( $r['action'] == 'illegal_page' ) - { - list($illegal_id, $illegal_ns) = unserialize($r['page_text']); - $url = makeUrlNS($illegal_ns, $illegal_id, false, true); - $title = get_page_title_ns($illegal_id, $illegal_ns); - $class = ( isPage($paths->nslist[$illegal_ns] . $illegal_id) ) ? '' : ' class="wikilink-nonexistent"'; - $illegal_link = '' . $title . ''; - } - if($cls == 'row2') $cls = 'row1'; - else $cls = 'row2'; - echo ''; - } - $db->free_result(); - } - echo '
TypeDateUsernameIP Address
Logs are recorded but not displayed for privacy purposes in the demo.
'; - switch($r['action']) - { - case "admin_auth_good": echo 'Successful elevated authentication'; if ( !empty($r['page_text']) ) { $level = $session->userlevel_to_string( intval($r['page_text']) ); echo "
Authentication level: $level"; } break; - case "admin_auth_bad": echo 'Failed elevated authentication'; if ( !empty($r['page_text']) ) { $level = $session->userlevel_to_string( intval($r['page_text']) ); echo "
Attempted auth level: $level"; } break; - case "activ_good": echo 'Successful account activation'; break; - case "auth_good": echo 'Successful regular user logon'; break; - case "activ_bad": echo 'Failed account activation'; break; - case "auth_bad": echo 'Failed regular user logon'; break; - case "sql_inject": echo 'SQL injection attempt
Offending query: ' . htmlspecialchars($r['page_text']) . '
'; break; - case "db_backup": echo 'Database backup created
Tables: ' . $r['page_text'] . ''; break; - case "install_enano": echo "Installed Enano version {$r['page_text']}"; break; - case "upgrade_enano": echo "Upgraded Enano to version {$r['page_text']}"; break; - case "illegal_page": echo "Unauthorized viewing attempt
Page: {$illegal_link}"; break; - } - echo '
'.date('d M Y h:i a', $r['time_id']).''.$r['author'].''.$r['edit_summary'].'
'; - if(!isset($_GET['fulllog'])) echo '

Full security log

'; + $seclog = get_security_log(5); + echo $seclog; + + echo '

Full security log

'; } @@ -488,12 +438,69 @@ if(isset($_POST['save'])) { - if(isset($_POST['enable_uploads'])) setConfig('enable_uploads', '1'); else setConfig('enable_uploads', '0'); - if(isset($_POST['enable_imagemagick'])) setConfig('enable_imagemagick', '1'); else setConfig('enable_imagemagick', '0'); - if(isset($_POST['cache_thumbs'])) setConfig('cache_thumbs', '1'); else setConfig('cache_thumbs', '0'); - if(isset($_POST['file_history'])) setConfig('file_history', '1'); else setConfig('file_history', '0'); - if(file_exists($_POST['imagemagick_path'])) setConfig('imagemagick_path', $_POST['imagemagick_path']); - else echo 'Warning: the file "'.$_POST['imagemagick_path'].'" was not found, and the ImageMagick file path was not updated.'; + if(isset($_POST['enable_uploads']) && getConfig('enable_uploads') != '1') + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","upload_enable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + setConfig('enable_uploads', '1'); + } + else if ( !isset($_POST['enable_uploads']) && getConfig('enable_uploads') == '1' ) + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","upload_disable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + setConfig('enable_uploads', '0'); + } + if(isset($_POST['enable_imagemagick']) && getConfig('enable_imagemagick') != '1') + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","magick_enable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + setConfig('enable_imagemagick', '1'); + } + else if ( !isset($_POST['enable_imagemagick']) && getConfig('enable_imagemagick') == '1' ) + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","magick_disable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + setConfig('enable_imagemagick', '0'); + } + if(isset($_POST['cache_thumbs'])) + { + setConfig('cache_thumbs', '1'); + } + else + { + setConfig('cache_thumbs', '0'); + } + if(isset($_POST['file_history']) && getConfig('file_history') != '1' ) + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","filehist_enable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + setConfig('file_history', '1'); + } + else if ( !isset($_POST['file_history']) && getConfig('file_history') == '1' ) + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","filehist_disable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + setConfig('file_history', '0'); + } + if(file_exists($_POST['imagemagick_path']) && $_POST['imagemagick_path'] != getConfig('imagemagick_path')) + { + $old = getConfig('imagemagick_path'); + $oldnew = "{$old}||{$_POST['imagemagick_path']}"; + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","magick_path",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($oldnew) . '");'); + if ( !$q ) + $db->_die(); + setConfig('imagemagick_path', $_POST['imagemagick_path']); + } + else if ( $_POST['imagemagick_path'] != getConfig('imagemagick_path') ) + { + echo 'Warning: the file "'.htmlspecialchars($_POST['imagemagick_path']).'" was not found, and the ImageMagick file path was not updated.'; + } $max_upload = floor((float)$_POST['max_file_size'] * (int)$_POST['fs_units']); if ( $max_upload > 1048576 && defined('ENANO_DEMO_MODE') ) { @@ -531,7 +538,7 @@

Lastly, you can choose whether file history will be saved. If this option is turned on, you will be able to roll back any malicious changes made to uploaded files, but this requires a significant amount of database storage. You should probably leave this option enabled unless you have less than 250MB of MySQL database space.

-

+


sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","plugin_enable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($_GET['plugin']) . '");'); + if ( !$q ) + $db->_die(); setConfig('plugin_'.$_GET['plugin'], '1'); break; case "disable": @@ -561,6 +571,9 @@ } if ( !in_array($_GET['plugin'], $plugins->system_plugins) ) { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,page_text) VALUES("security","plugin_disable",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '","' . $db->escape($_GET['plugin']) . '");'); + if ( !$q ) + $db->_die(); setConfig('plugin_'.$_GET['plugin'], '0'); } else diff -r 1c7f59df9474 -r 93ef7df77847 plugins/admin/SecurityLog.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/admin/SecurityLog.php Wed Aug 29 23:35:06 2007 -0400 @@ -0,0 +1,160 @@ +auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN ) + { + echo '

Error: Not authenticated

It looks like your administration session is invalid or you are not authorized to access this administration page. Please re-authenticate to continue.

'; + return; + } + + if ( defined('ENANO_DEMO_MODE') ) + { + die('Security log is disabled in demo mode.'); + } + + echo '

System security log

'; + + // Not calling the real fetcher because we have to paginate the results + $offset = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0; + $q = $db->sql_query('SELECT COUNT(time_id) as num FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC;'); + if ( !$q ) + $db->_die(); + $row = $db->fetchrow(); + $db->free_result(); + $count = intval($row['num']); + $q = $db->sql_unbuffered_query('SELECT action,date_string,author,edit_summary,time_id,page_text FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC;'); + if ( !$q ) + $db->_die(); + + $html = paginate( + $q, + '{time_id}', + $count, + makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'SecurityLog&offset=%s'), + $offset, + 50, + array('time_id' => 'seclog_format_inner'), + '
+ ', + '
TypeDateUsernameIP Address
' + ); + + echo $html; + +} + +function get_security_log($num = false) +{ + global $db, $session, $paths, $template, $plugins; // Common objects + if ( $session->auth_level < USER_LEVEL_ADMIN ) + { + $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author) VALUES("security","seclog_unauth",UNIX_TIMESTAMP(),"' . $db->escape($_SERVER['REMOTE_ADDR']) . '","' . $db->escape($session->username) . '");'); + if ( !$q ) + $db->_die(); + die('Security log: unauthorized attempt to fetch. Call has been logged and reported to the administrators.'); + } + + $return = '
'; + $cls = 'row2'; + $return .= ''; + $hash = sha1(microtime()); + if ( defined('ENANO_DEMO_MODE') ) + { + require('config.php'); + $hash = md5($dbpasswd); + unset($dbname, $dbhost, $dbuser, $dbpasswd); + unset($dbname, $dbhost, $dbuser, $dbpasswd); // PHP5 Zend bug + } + if ( defined('ENANO_DEMO_MODE') && !isset($_GET[ $hash ]) && substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' ) + { + $return .= ''; + } + else + { + if(is_int($num)) + { + $l = 'SELECT action,date_string,author,edit_summary,time_id,page_text FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC LIMIT '.$num.';'; + } + else + { + $l = 'SELECT action,date_string,author,edit_summary,time_id,page_text FROM '.table_prefix.'logs WHERE log_type=\'security\' ORDER BY time_id DESC, action ASC;'; + } + $q = $db->sql_query($l); + while($r = $db->fetchrow()) + { + $return .= seclog_format_inner($r); + } + $db->free_result(); + } + $return .= '
TypeDateUsernameIP Address
Logs are recorded but not displayed for privacy purposes in the demo.
'; + + return $return; +} + +function seclog_format_inner($r, $f = false) +{ + if ( is_array($f) ) + { + unset($r); + $r =& $f; + } + global $db, $session, $paths, $template, $plugins; // Common objects + $return = ''; + static $cls = 'row2'; + if ( $r['action'] == 'illegal_page' ) + { + list($illegal_id, $illegal_ns) = unserialize($r['page_text']); + $url = makeUrlNS($illegal_ns, $illegal_id, false, true); + $title = get_page_title_ns($illegal_id, $illegal_ns); + $class = ( isPage($paths->nslist[$illegal_ns] . $illegal_id) ) ? '' : ' class="wikilink-nonexistent"'; + $illegal_link = '' . $title . ''; + } + else if ( $r['action'] == 'plugin_enable' || $r['action'] == 'plugin_disable' ) + { + $row['page_text'] = htmlspecialchars($row['page_text']); + } + $cls = ( $cls == 'row2' ) ? 'row1' : 'row2'; + $return .= ''; + switch($r['action']) + { + case "admin_auth_good": $return .= 'Successful elevated authentication'; if ( !empty($r['page_text']) ) { $level = $session->userlevel_to_string( intval($r['page_text']) ); $return .= "
Authentication level: $level"; } break; + case "admin_auth_bad": $return .= 'Failed elevated authentication'; if ( !empty($r['page_text']) ) { $level = $session->userlevel_to_string( intval($r['page_text']) ); $return .= "
Attempted auth level: $level"; } break; + case "activ_good": $return .= 'Successful account activation'; break; + case "auth_good": $return .= 'Successful regular user logon'; break; + case "activ_bad": $return .= 'Failed account activation'; break; + case "auth_bad": $return .= 'Failed regular user logon'; break; + case "sql_inject": $return .= 'SQL injection attempt
Offending query: ' . htmlspecialchars($r['page_text']) . '
'; break; + case "db_backup": $return .= 'Database backup created
Tables: ' . $r['page_text'] . ''; break; + case "install_enano": $return .= "Installed Enano version {$r['page_text']}"; break; + case "upgrade_enano": $return .= "Upgraded Enano to version {$r['page_text']}"; break; + case "illegal_page": $return .= "Unauthorized viewing attempt
Page: {$illegal_link}"; break; + case "upload_enable": $return .= "Enabled file uploads"; break; + case "upload_disable": $return .= "Disabled file uploads"; break; + case "magick_enable": $return .= "Enabled ImageMagick for uploaded images"; break; + case "magick_disable": $return .= "Disabled ImageMagick for uploaded images"; break; + case "filehist_enable": $return .= "Enabled revision tracking for uploaded files"; break; + case "filehist_disable": $return .= "Disabled revision tracking for uploaded files"; break; + case "magick_path": $return .= "Changed path to ImageMagick executable"; break; + case "plugin_disable": $return .= "Disabled plugin: {$r['page_text']}"; break; + case "plugin_enable": $return .= "Enabled plugin: {$r['page_text']}"; break; + case "seclog_unauth": $return .= "Unauthorized attempt to call security log fetcher"; break; + } + $return .= ''.date('d M Y h:i a', $r['time_id']).''.$r['author'].''.$r['edit_summary'].''; + return $return; +} + +?>