From 233903c84428b9322eaea94bf22f6ae972e44332 Mon Sep 17 00:00:00 2001 From: Klaus Weidenbach Date: Sun, 14 Dec 2014 01:22:52 +0100 Subject: Add security logger to RedDAV. Some smaller clean ups whitepsaces and tabs, use PHP_EOL, Doxygen, etc. --- include/RedDAV/RedBasicAuth.php | 7 +- include/RedDAV/RedBrowser.php | 9 +- include/auth.php | 22 ++- include/text.php | 409 +++++++++++++++++++--------------------- 4 files changed, 221 insertions(+), 226 deletions(-) (limited to 'include') diff --git a/include/RedDAV/RedBasicAuth.php b/include/RedDAV/RedBasicAuth.php index 2f86d4f82..19dd9a5f0 100644 --- a/include/RedDAV/RedBasicAuth.php +++ b/include/RedDAV/RedBasicAuth.php @@ -118,8 +118,11 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic { } } } - logger('password failed for ' . $username); - // @TODO add security logger + + $error = 'password failed for ' . $username; + logger($error); + log_failed_login($error); + return false; } diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php index 21ea76aed..eb08fd79f 100644 --- a/include/RedDAV/RedBrowser.php +++ b/include/RedDAV/RedBrowser.php @@ -182,10 +182,10 @@ class RedBrowser extends DAV\Browser\Plugin { } } } - - $parentHash = ""; + + $parentHash = ''; $owner = $this->auth->owner_id; - $splitPath = split("/", $fullPath); + $splitPath = split('/', $fullPath); if (count($splitPath) > 3) { for ($i = 3; $i < count($splitPath); $i++) { $attachName = urldecode($splitPath[$i]); @@ -233,6 +233,7 @@ class RedBrowser extends DAV\Browser\Plugin { } // prepare quota for template + $quota = array(); $quota['used'] = $used; $quota['limit'] = $limit; $quota['desc'] = $quotaDesc; @@ -257,7 +258,7 @@ class RedBrowser extends DAV\Browser\Plugin { $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); } $html .= $output; - + get_app()->page['content'] = $html; load_pdl(get_app()); construct_page(get_app()); diff --git a/include/auth.php b/include/auth.php index 94c64e58d..545fbe8c9 100644 --- a/include/auth.php +++ b/include/auth.php @@ -41,6 +41,9 @@ function nuke_session() { /** * @brief Verify login credentials. * + * If system authlog is set a log entry will be added for failed login + * attempts. + * * @param string $email * The email address to verify. * @param string $pass @@ -88,14 +91,25 @@ function account_verify_password($email, $pass) { if($record['account_flags'] & ACCOUNT_PENDING) logger('Account is pending. account_flags = ' . $record['account_flags']); - // Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention - $authlog = get_config('system', 'authlog'); - if ($authlog) - @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); + log_failed_login($error); return null; } +/** + * @brief Log failed logins to a separate auth log. + * + * Can be used to reduce overhead for server side intrusion prevention, like + * parse the authlog file with something like fail2ban, OSSEC, etc. + * + * @param string $errormsg + * Error message to display for failed login. + */ +function log_failed_login($errormsg) { + $authlog = get_config('system', 'authlog'); + if ($authlog) + @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $errormsg . PHP_EOL, FILE_APPEND); +} /** * Inline - not a function diff --git a/include/text.php b/include/text.php index 70a8f1179..3a7c02362 100644 --- a/include/text.php +++ b/include/text.php @@ -181,34 +181,34 @@ function autoname($len) { 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh'); $start = mt_rand(0,2); - if($start == 0) - $table = $vowels; - else - $table = $cons; + if($start == 0) + $table = $vowels; + else + $table = $cons; $word = ''; for ($x = 0; $x < $len; $x ++) { - $r = mt_rand(0,count($table) - 1); - $word .= $table[$r]; - - if($table == $vowels) - $table = array_merge($cons,$midcons); - else - $table = $vowels; + $r = mt_rand(0,count($table) - 1); + $word .= $table[$r]; + + if($table == $vowels) + $table = array_merge($cons,$midcons); + else + $table = $vowels; } $word = substr($word,0,$len); foreach($noend as $noe) { - if((strlen($word) > 2) && (substr($word,-2) == $noe)) { - $word = substr($word,0,-1); - break; - } + if((strlen($word) > 2) && (substr($word,-2) == $noe)) { + $word = substr($word,0,-1); + break; + } } if(substr($word,-1) == 'q') - $word = substr($word,0,-1); + $word = substr($word,0,-1); return $word; } @@ -224,11 +224,11 @@ function autoname($len) { */ function xmlify($str) { $buffer = ''; - + $len = mb_strlen($str); for($x = 0; $x < $len; $x ++) { $char = mb_substr($str,$x,1); - + switch( $char ) { case "\r" : @@ -267,7 +267,7 @@ function xmlify($str) { function unxmlify($s) { $ret = str_replace('&','&', $s); $ret = str_replace(array('<','>','"','''),array('<','>','"',"'"),$ret); - return $ret; + return $ret; } // convenience wrapper, reverse the operation "bin2hex" @@ -314,8 +314,7 @@ function paginate(&$a) { $pagenum = $a->pager['page']; $url = $a->get_baseurl() . '/' . $stripped; - - if($a->pager['total'] > $a->pager['itemspage']) { + if($a->pager['total'] > $a->pager['itemspage']) { $o .= '
'; if($a->pager['page'] != 1) $o .= ''."pager['page'] - 1).'">' . t('prev') . ' '; @@ -331,7 +330,7 @@ function paginate(&$a) { $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); } - + for($i = $numstart; $i <= $numstop; $i++){ if($i == $a->pager['page']) $o .= ''.(($i < 10) ? ' '.$i : $i); @@ -405,7 +404,7 @@ function expand_acl($s) { } } return $ret; -} +} // Used to wrap ACL elements in angle brackets for storage @@ -420,7 +419,6 @@ function sanitise_acl(&$item) { // Convert an ACL array to a storable string - function perms2str($p) { $ret = ''; @@ -436,16 +434,17 @@ function perms2str($p) { return $ret; } -// generate a guaranteed unique (for this domain) item ID for ATOM -// safe from birthday paradox - - +/** + * @brief Generate a guaranteed unique (for this domain) item ID for ATOM. + * + * Safe from birthday paradox. + * + * @return string a unique id + */ function item_message_id() { - do { $dups = false; $hash = random_string(); - $mid = $hash . '@' . get_app()->get_hostname(); $r = q("SELECT id FROM item WHERE mid = '%s' LIMIT 1", @@ -453,31 +452,33 @@ function item_message_id() { if(count($r)) $dups = true; } while($dups == true); + return $mid; } -// Generate a guaranteed unique photo ID. -// safe from birthday paradox - - +/** + * @brief Generate a guaranteed unique photo ID. + * + * Safe from birthday paradox. + * + * @return string a uniqe hash + */ function photo_new_resource() { - do { $found = false; - $resource = hash('md5',uniqid(mt_rand(),true)); + $resource = hash('md5', uniqid(mt_rand(), true)); + $r = q("SELECT id FROM photo WHERE resource_id = '%s' LIMIT 1", - dbesc($resource) - ); + dbesc($resource)); if(count($r)) $found = true; - } while($found == true); + } while($found === true); + return $resource; } - - // for html,xml parsing - let's say you've got // an attribute foobar="class1 class2 class3" // and you want to find out if it contains 'class3'. @@ -487,52 +488,76 @@ function photo_new_resource() { // pass the attribute string as $attr and the attribute you // are looking for as $s - returns true if found, otherwise false -function attribute_contains($attr,$s) { +function attribute_contains($attr, $s) { $a = explode(' ', $attr); - if(count($a) && in_array($s,$a)) + if(count($a) && in_array($s, $a)) return true; + return false; } - -function logger($msg,$level = 0) { +/** + * @brief Logging function for RedMatrix. + * + * Logging output is configured through RedMatrix's system config. The log file + * is set in system logfile, log level in system loglevel and to enable logging + * set system debugging. + * + * Available constants for log level are LOGGER_NORMAL, LOGGER_TRACE, LOGGER_DEBUG, + * LOGGER_DATA and LOGGER_ALL. + * + * Since PHP5.4 we get the file, function and line automatically where the logger + * was caleld, so no need to add it to the message anymore. + * + * @param string $msg Message to log + * @param int $level A log level. + */ +function logger($msg, $level = 0) { // turn off logger in install mode global $a; global $db; - if(($a->module == 'install') || (! ($db && $db->connected))) return; + if(($a->module == 'install') || (! ($db && $db->connected))) + return; - $debugging = get_config('system','debugging'); - $loglevel = intval(get_config('system','loglevel')); - $logfile = get_config('system','logfile'); + $debugging = get_config('system', 'debugging'); + $loglevel = intval(get_config('system', 'loglevel')); + $logfile = get_config('system', 'logfile'); if((! $debugging) || (! $logfile) || ($level > $loglevel)) return; $where = ''; - if(version_compare(PHP_VERSION,'5.4.0') >= 0) { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2); + if(version_compare(PHP_VERSION, '5.4.0') >= 0) { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; } - @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . "\n", FILE_APPEND); - return; + @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); } - -// This is a special logging facility for developers. It allows one to target specific things to trace/debug -// and is identical to logger() with the exception of the log filename. This allows one to isolate specific -// calls while allowing logger() to paint a bigger picture of overall activity and capture more detail. -// If you find dlogger() calls in checked in code, you are free to remove them - so as to provide a noise-free -// development environment which responds to events you are targetting personally. - - -function dlogger($msg,$level = 0) { +/** + * @brief This is a special logging facility for developers. + * + * It allows one to target specific things to trace/debug and is identical to + * logger() with the exception of the log filename. This allows one to isolate + * specific calls while allowing logger() to paint a bigger picture of overall + * activity and capture more detail. + * + * If you find dlogger() calls in checked in code, you are free to remove them - + * so as to provide a noise-free development environment which responds to events + * you are targetting personally. + * + * @param string $msg Message to log + * @param int $level A log level. + */ +function dlogger($msg, $level = 0) { // turn off logger in install mode global $a; global $db; - if(($a->module == 'install') || (! ($db && $db->connected))) return; + if(($a->module == 'install') || (! ($db && $db->connected))) + return; $debugging = get_config('system','debugging'); $loglevel = intval(get_config('system','loglevel')); @@ -540,19 +565,23 @@ function dlogger($msg,$level = 0) { if((! $debugging) || (! $logfile) || ($level > $loglevel)) return; - - @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND); - return; + + $where = ''; + if(version_compare(PHP_VERSION, '5.4.0') >= 0) { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; + } + + @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL, FILE_APPEND); } function profiler($t1,$t2,$label) { if(file_exists('profiler.out') && $t1 && t2) - @file_put_contents('profiler.out', sprintf('%01.4f %s',$t2 - $t1,$label) . "\n", FILE_APPEND); + @file_put_contents('profiler.out', sprintf('%01.4f %s',$t2 - $t1,$label) . PHP_EOL, FILE_APPEND); } - function activity_match($haystack,$needle) { if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA))) return true; @@ -569,7 +598,6 @@ function activity_match($haystack,$needle) { // Returns array of tags found, or empty array. - function get_tags($s) { $ret = array(); @@ -592,9 +620,6 @@ function get_tags($s) { // Match full names against @tags including the space between first and last // We will look these up afterward to see if they are full names or not recognisable. - - - if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) { foreach($match[1] as $mtch) { if(strstr($mtch,"]")) { @@ -648,7 +673,6 @@ function get_tags($s) { usort($ret,'tag_sort_length'); - // logger('get_tags: ' . print_r($ret,true)); return $ret; @@ -657,13 +681,12 @@ function get_tags($s) { function tag_sort_length($a,$b) { if(mb_strlen($a) == mb_strlen($b)) return 0; + return((mb_strlen($b) < mb_strlen($a)) ? (-1) : 1); } - - function strip_zids($s) { return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s); } @@ -673,12 +696,10 @@ function strip_zids($s) { function qp($s) { -return str_replace ("%","=",rawurlencode($s)); + return str_replace ("%","=",rawurlencode($s)); } - - function get_mentions($item,$tags) { $o = ''; @@ -712,7 +733,6 @@ function contact_block() { if($shown == 0) return; - $is_owner = ((local_user() && local_user() == $a->profile['uid']) ? true : false); $abook_flags = ABOOK_FLAG_PENDING|ABOOK_FLAG_SELF; @@ -734,8 +754,7 @@ function contact_block() { } if(! $total) { $contacts = t('No connections'); - $micropro = Null; - + $micropro = null; } else { if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { $randfunc = 'RANDOM()'; @@ -758,7 +777,7 @@ function contact_block() { } } } - + $tpl = get_markup_template('contact_block.tpl'); $o = replace_macros($tpl, array( '$contacts' => $contacts, @@ -771,7 +790,6 @@ function contact_block() { call_hooks('contact_block_end', $arr); return $o; - } @@ -815,8 +833,6 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { } - - function search($s,$id='search-box',$url='/search',$save = false) { $a = get_app(); return replace_macros(get_markup_template('searchbox.tpl'),array( @@ -843,12 +859,12 @@ function searchbox($s,$id='search-box',$url='/search',$save = false) { function valid_email($x){ - if(get_config('system','disable_email_validation')) return true; if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x)) return true; + return false; } @@ -879,11 +895,10 @@ function linkify($s) { * * @returns string */ - - function sslify($s) { if(strpos(z_root(),'https:') === false) return $s; + $matches = null; $cnt = preg_match_all("/\<(.*?)src=\"(http\:.*?)\"(.*?)\>/",$s,$matches,PREG_SET_ORDER); if($cnt) { @@ -897,7 +912,6 @@ function sslify($s) { function get_poke_verbs() { - // index is present tense verb // value is array containing past tense verb, translation of present, translation of past @@ -909,12 +923,13 @@ function get_poke_verbs() { 'finger' => array( 'fingered', t('finger'), t('fingered')), 'rebuff' => array( 'rebuffed', t('rebuff'), t('rebuffed')), ); + call_hooks('poke_verbs', $arr); return $arr; } function get_mood_verbs() { - + $arr = array( 'happy' => t('happy'), 'sad' => t('sad'), @@ -964,17 +979,13 @@ function get_mood_verbs() { * bbcode source for HTML display * */ - - function smilies($s, $sample = false) { - $a = get_app(); if(intval(get_config('system','no_smilies')) || (local_user() && intval(get_pconfig(local_user(),'system','no_smilies')))) return $s; - $s = preg_replace_callback('{<(pre|code)>.*?}ism','smile_shield',$s); $s = preg_replace_callback('/<[a-z]+ .*?>/ism','smile_shield',$s); @@ -1072,7 +1083,6 @@ function smilies($s, $sample = false) { $s = preg_replace_callback('//ism', 'smile_unshield', $s); return $s; - } function smile_shield($m) { @@ -1132,6 +1142,7 @@ function normalise_link($url) { function link_compare($a,$b) { if(strcasecmp(normalise_link($a),normalise_link($b)) === 0) return true; + return false; } @@ -1147,7 +1158,6 @@ function unobscure(&$item) { if($item['body']) $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']),$key); } - } function theme_attachments(&$item) { @@ -1190,12 +1200,10 @@ function theme_attachments(&$item) { $url = $r['href']; else $url = z_root() . '/magic?f=&hash=' . $item['author_xchan'] . '&dest=' . $r['href'] . '/' . $r['revision']; + $s .= '' . $icon . ''; $attaches[] = array('title' => $title, 'url' => $url, 'icon' => $icon ); - } - - } $s = replace_macros(get_markup_template('item_attach.tpl'), array( @@ -1203,13 +1211,12 @@ function theme_attachments(&$item) { )); return $s; - } function format_categories(&$item,$writeable) { - $s = ''; + $terms = get_terms_oftype($item['term'],TERM_CATEGORY); if($terms) { $categories = array(); @@ -1225,6 +1232,7 @@ function format_categories(&$item,$writeable) { '$remove' => t('remove category'), '$categories' => $categories )); + return $s; } @@ -1235,7 +1243,6 @@ function format_hashtags(&$item) { $s = ''; $terms = get_terms_oftype($item['term'],TERM_HASHTAG); if($terms) { - $categories = array(); foreach($terms as $t) { $term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ; if(! trim($term)) @@ -1255,11 +1262,10 @@ function format_hashtags(&$item) { function format_mentions(&$item) { - $s = ''; + $terms = get_terms_oftype($item['term'],TERM_MENTION); if($terms) { - $categories = array(); foreach($terms as $t) { $term = htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8',false) ; if(! trim($term)) @@ -1278,8 +1284,8 @@ function format_mentions(&$item) { function format_filer(&$item) { - $s = ''; + $terms = get_terms_oftype($item['term'],TERM_FILE); if($terms) { $categories = array(); @@ -1295,19 +1301,14 @@ function format_filer(&$item) { '$remove' => t('remove from file'), '$categories' => $categories )); + return $s; } - - function prepare_body(&$item,$attach = false) { - $a = get_app(); - - - call_hooks('prepare_body_init', $item); unobscure($item); @@ -1322,28 +1323,22 @@ function prepare_body(&$item,$attach = false) { return $s; } - $s .= theme_attachments($item); - - $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false); - + $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false); $s .= format_hashtags($item); if($item['resource_type']) $s .= format_mentions($item); - $s .= format_categories($item,$writeable); if(local_user() == $item['uid']) $s .= format_filer($item); - $s = sslify($s); - // Look for spoiler $spoilersearch = '
'; @@ -1358,7 +1353,7 @@ function prepare_body(&$item,$attach = false) { $pos = strpos($s, $spoilersearch); $rnd = random_string(8); $spoilerreplace = '
'.sprintf(t('Click to open/close')).''. - '