Tuesday, January 15, 2013

Tracking logon events to Owncloud with Powershell

If you have followed my blog recently, you already know that I am interested in the new on-premise cloud solution names Owncloud. I have spent many hours testing this software and, though young, it has some interesting features which make me willing to keep going on with it. Unfortunately for me, the version running on Windows Server has many known bugs and many missing features. 

In this post I will detail how to force OwnCloud on Windows to keep a log of authentication events, which is a feature of primary importance to me. To do it, I'll show you how to modify the PHP code, as well as how to put in place a Powershell scheduled task aimed to sending e-mail alerts upon authentication events.

First of all, let me state that I am using OwnCloud version 4.5.5, which is the last one at the moment of writing. In this version the code that handles logon events is stored inside a file named base.php, under C:\inetpub\wwwroot\owncloud\lib\ folder.

In this file there is a function named tryFormLogin(), which starts at line 539 end ends at line 565. The original code is pretty simple but allow for easy modifications.

The modifications to make are listed here below. You can just copy and paste them in the PHP code of your base.php file using any advanced text editor (but not Notepad, use Notepad++ instead!).
protected static function tryFormLogin() {
if(!isset($_POST["user"]) || !isset($_POST['password'])) {
return false;
}
OC_App::loadApps();
//setup extra user backends
OC_User::setupBackends();
if(OC_User::login($_POST["user"], $_POST["password"])) {
if(!empty($_POST["remember_login"])) {
if(defined("DEBUG") && DEBUG) {
OC_Log::write('core', 'Setting remember login to cookie', OC_Log::DEBUG);
}
$token = md5($_POST["user"].time().$_POST['password']);
OC_Preferences::setValue($_POST['user'], 'login', 'token', $token);
OC_User::setMagicInCookie($_POST["user"], $token);
}
else {
OC_User::unsetMagicInCookie();
}
header( 'Location: '.$_SERVER['REQUEST_URI'] );
$postuser = $_POST["user"];
$Current_Date = new DateTime();
date_timezone_set($Current_Date, timezone_open('Europe/London'));
$Remote_Address= $_SERVER['REMOTE_ADDR'];
$Authentication_Log = fopen('d:/auth.log', 'a+');
fputs($Authentication_Log, date_format($Current_Date, 'Y/m/d H:i:s') . " \tServer name " . " Successful login to your Owncloud by : " . $postuser . " \t" . $Remote_Address . "\n");
fclose($Authentication_Log);
exit();
}
else
{
$postuser = $_POST["user"];
$Current_Date = new DateTime();
date_timezone_set($Current_Date, timezone_open('Europe/London'));
$Remote_Address= $_SERVER['REMOTE_ADDR'];
$Authentication_Log = fopen('d:/auth.log', 'a+');
fputs($Authentication_Log, date_format($Current_Date, 'Y/m/d H:i:s') . " \tServer name " . " Failed login to your Owncloud by : " . $postuser . " \t" . $Remote_Address . "\n");
fclose($Authentication_Log);
}
return true;
}
This script will save successful logins as well as logon failures to a file named d:\auth.log. This file will be formatted with one logon event per line. The format used for showing the actual time of the event is Y/m/d H:i:s, where:
  • Y is the year in 4 ciphers; i.e. "2013" 
  • m is the mont in numerical form, i.e. "02" for February 
  • d is the day of the month, i.e. "27" 
  • H is the hour in 24 hour format 
  • i is the minute "00 to "59" 
  • s is the second "00 to "59"
You might be wondering the reason why do I write a list of this fields. Well, in my solution, this formats must be translated to types understandables by the .Net function DateTime.ParseExact(), which we will call in the Powershell script that I show further in this post.

The function DateTime.ParseExact() allows you to parse a custom date/time format and takes three parameters. the first one is the original string, the second one is the type of formatting to apply, and the third one is the provider used to change the representation of the date and time value.

In our case an authentication event triggers the creation (if it exists, it does append) of the authetication log where one line is added in the following format:

2013/01/15 14:17:18 Server name owncloud: Failed login to your Owncloud by : intruder x.x.x.x

The first field (chars 0 to 19) is the time of the event.

The afore-mentioned PHP fields translate in Powershell to:
  • Y to yyyy 
  • m to MM 
  • d to dd 
  • H to HH 
  • i to mm 
  • s to ss
and 2013/01/15 14:17:18 translates to 'yyyy/MM/dd HH:mm:ss' in Powershell. Be aware that this fields are case sensitive.

Not so simple nor evident, isn't it?

Here's the Powershell script:

# Scheduled task every 15 minutes
# Checking for successful logons
gc D:\auth.log | %{if($_ -match "Successful login")
{
$when = [datetime]::ParseExact($_.substring(0,19),"yyyy/MM/dd HH:mm:ss",$null) 
if((get-date).addminutes(-15) -lt $when)
{
# Access granted in the last 15 minutes. Ok.
Send-MailMessage -From owncloud@yourserver.com -SmtpServer smtpserver.com -Subject $_ -To "you@yourmailserver.com"
}
} 
}
# Checking for failed logons
gc D:\auth.log | %{if($_ -match "Failed login")
{
$when = [datetime]::ParseExact($_.substring(0,19),"yyyy/MM/dd HH:mm:ss",$null) 
if((get-date).addminutes(-15) -lt $when)
{
# Intrusion in the last 15 minutes!
Send-MailMessage -From owncloud@yourserver.com -SmtpServer smtpserver.com -Subject $_ -To "you@yourmailserver.com"
}
} 
}
# End
Now we just need to make a scheduled tasks that runs this script every 15 minutes and we will receive an e-mail on any logon events, be it successful or failed. You could also trigger other actions if you want. Send-Mailmessage is just one example. The important is to be warned if someone tries to sneak in.

I hope you like my solution, it took a long time to make it work, so do not hesitate to comment or Google+. Of course I am not a software engineer. This is just the solution of a system administrator willing to improve the security of its deployments. Maybe someone between the readers can suggest a better approach.

2 comments:

  1. Thanks for the code!
    I'm just using the base.ini-part of it and noticed that the order of the commands results in a php warning and wrong timezones:
    $Current_Date = new DateTime();
    should be before
    date_timezone_set($Current_Date, timezone_open('Europe/London'));
    in both cases.
    Regards,
    kokomo

    ReplyDelete
  2. Thnaks for the correction, Kokomo!

    I have updated the script.

    May I ask you why don't you use the powershell part? Do you manually check your authentication log manually?

    Carlo

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...