Merge pull request #741 from dawnbreak/docu

Add security logger to RedDAV.
This commit is contained in:
Thomas Willingham 2014-12-14 12:26:08 +00:00
commit 082ef8b950
5 changed files with 237 additions and 233 deletions

View File

@ -51,6 +51,15 @@ define ( 'ZOT_REVISION', 1 );
define ( 'DB_UPDATE_VERSION', 1131 );
/**
* Constant with a HTML line break.
*
* Contains a HTML line break (br) element and a real carriage return with line
* feed for the source.
* This can be used in HTML and JavaScript where needed a line break.
*
* @var string
*/
define ( 'EOL', '<br>' . "\r\n" );
define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );
//define ( 'NULL_DATE', '0000-00-00 00:00:00' );

View File

@ -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;
}

View File

@ -183,9 +183,9 @@ 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;

View File

@ -41,6 +41,9 @@ function nuke_session() {
/**
* @brief Verify login credentials.
*
* If system <i>authlog</i> 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

View File

@ -314,7 +314,6 @@ function paginate(&$a) {
$pagenum = $a->pager['page'];
$url = $a->get_baseurl() . '/' . $stripped;
if($a->pager['total'] > $a->pager['itemspage']) {
$o .= '<div class="pager">';
if($a->pager['page'] != 1)
@ -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'));
@ -541,18 +566,22 @@ 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()';
@ -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,6 +923,7 @@ 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;
}
@ -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)>.*?</\1>}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('/<!--base64:(.*?)-->/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 .= '<a href="' . $url . '" title="' . $title . '" class="attachlink" >' . $icon . '</a>';
$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);
$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 = '<blockquote class="spoiler">';
@ -1366,7 +1361,6 @@ function prepare_body(&$item,$attach = false) {
$authorsearch = '<blockquote class="author">';
while ((strpos($s, $authorsearch) !== false)) {
$pos = strpos($s, $authorsearch);
$rnd = random_string(8);
$authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'.
@ -1386,10 +1380,7 @@ function prepare_body(&$item,$attach = false) {
function prepare_text($text,$content_type = 'text/bbcode') {
switch($content_type) {
case 'text/plain':
$s = escape_tags($text);
break;
@ -1472,14 +1463,11 @@ function zidify_links($s) {
}
/**
* return atom link elements for all of our hubs
*/
function feed_hublinks() {
$hub = get_config('system','huburl');
$hubxml = '';
@ -1494,14 +1482,13 @@ function feed_hublinks() {
}
}
}
return $hubxml;
}
/* return atom link elements for salmon endpoints */
function feed_salmonlinks($nick) {
$a = get_app();
$salmon = '<link rel="salmon" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
@ -1510,12 +1497,12 @@ function feed_salmonlinks($nick) {
$salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
$salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ;
return $salmon;
}
function get_plink($item,$conversation_mode = true) {
$a = get_app();
if($conversation_mode)
$key = 'plink';
else
@ -1558,9 +1545,6 @@ function layout_select($channel_id, $current = '') {
}
function mimetype_select($channel_id, $current = 'text/bbcode') {
$x = array(
@ -1591,11 +1575,9 @@ function mimetype_select($channel_id, $current = 'text/bbcode') {
$o .= '</select>';
return $o;
}
function lang_selector() {
global $a;
@ -1627,14 +1609,13 @@ function lang_selector() {
'$langs' => array($lang_options, $selected),
));
return $o;
}
function return_bytes ($size_str) {
switch (substr ($size_str, -1))
{
switch (substr ($size_str, -1)) {
case 'M': case 'm': return (int)$size_str * 1048576;
case 'K': case 'k': return (int)$size_str * 1024;
case 'G': case 'g': return (int)$size_str * 1073741824;
@ -1654,7 +1635,6 @@ function base64url_encode($s, $strip_padding = true) {
}
function base64url_decode($s) {
if(is_array($s)) {
logger('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
return $s;
@ -1662,7 +1642,11 @@ function base64url_decode($s) {
return base64_decode(strtr($s,'-_','+/'));
}
/**
* @ Return a div to clear floats.
*
* @return string
*/
function cleardiv() {
return '<div class="clear"></div>';
}
@ -1700,15 +1684,14 @@ function html2bb_video($s) {
/**
* apply xmlify() to all values of array $val, recursively
*/
function array_xmlify($val){
function array_xmlify($val) {
if (is_bool($val)) return $val?"true":"false";
if (is_array($val)) return array_map('array_xmlify', $val);
return xmlify((string) $val);
}
function reltoabs($text, $base)
{
function reltoabs($text, $base) {
if (empty($base))
return $text;
@ -1734,14 +1717,11 @@ function reltoabs($text, $base)
$replace = "<img\${1} src=\"" . $base . "\${2}\"";
$text = preg_replace($pattern, $replace, $text);
// Done
return $text;
}
function item_post_type($item) {
switch($item['resource_type']) {
case 'photo':
$post_type = t('photo');
@ -1796,6 +1776,7 @@ function is_a_date_arg($s) {
return true;
}
}
return false;
}
@ -1847,6 +1828,7 @@ function check_webbie($arr) {
}
}
}
return '';
}
@ -1855,6 +1837,7 @@ function ids_to_querystr($arr,$idx = 'id') {
$t = array();
foreach($arr as $x)
$t[] = $x[$idx];
return(implode(',', $t));
}
@ -1903,7 +1886,6 @@ function xchan_query(&$items,$abook = true,$effective_uid = 0) {
$items[$x]['author'] = find_xchan_in_array($items[$x]['author_xchan'],$chans);
}
}
}
function xchan_mail_query(&$item) {
@ -1943,6 +1925,7 @@ function get_rel_link($j,$rel) {
foreach($j as $l)
if($l['rel'] === $rel)
return $l['href'];
return '';
}
@ -1978,7 +1961,6 @@ function jindent($json) {
$outOfQuotes = true;
for ($i=0; $i<=$strLen; $i++) {
// Grab the next character in the string.
$char = substr($json, $i, 1);
@ -2020,12 +2002,11 @@ function jindent($json) {
function json_decode_plus($s) {
$x = json_decode($s,true);
if(! $x)
$x = json_decode(str_replace(array('\\"','\\\\'),array('"','\\'),$s),true);
return $x;
return $x;
}
@ -2105,12 +2086,10 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag) {
$replaced = false;
$r = null;
$termtype = ((strpos($tag,'#') === 0) ? TERM_HASHTAG : TERM_UNKNOWN);
$termtype = ((strpos($tag,'@') === 0) ? TERM_MENTION : $termtype);
$termtype = ((strpos($tag,'#^[') === 0) ? TERM_BOOKMARK : $termtype);
//is it a hash tag?
if(strpos($tag,'#') === 0) {
if(strpos($tag,'#^[') === 0) {
@ -2118,7 +2097,6 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag) {
$basetag = $match[3];
$url = ((substr($match[2],0,1) === '=') ? substr($match[2],1) : $match[3]);
$replaced = true;
}
}
// if the tag is already replaced...
@ -2156,6 +2134,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag) {
//append or set str_tags
if(strlen($str_tags))
$str_tags .= ',';
$str_tags .= $newtag;
}
return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $basetag, 'url' => $url, 'contact' => $r[0]);
@ -2264,7 +2243,6 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag) {
}
if(! $r) {
// it's possible somebody has a name ending with '+', which we stripped off as a forum indicator
// This is very rare but we want to get it right.
@ -2340,6 +2318,5 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag) {
}
}
return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $newname, 'url' => $url, 'contact' => $r[0]);
}