[pptp-server] log analysing

Phil Verghese philv at ridgerun.com
Mon Sep 11 16:12:20 CDT 2000


Here's a script I wrote to look through the logs and generate a report.
This is my first significant Perl script, so forgive me if it's not
optimal.

Phil

------------------------------------------------------

#!/usr/bin/perl
#
# pptplog.pl
# This program parses /var/log/messages looking for login attempts
# through pptpd & pppd to generate a report of failed, insecure and
# valid login attempts
#
# USAGE: pptplog [-f filename] [-kh]
# -f     Specifies log file to read (default is /var/log/messages)
# -k     Keep temporary files in /tmp (default is to delete these files)
# -n     Do not send any mail (default is to send to root)
# -h     Show usage
#
# Copyright (C) 2000 Phil Verghese <Phil_Verghese at bigfoot.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
USA,
# or visit http://www.gnu.org/copyleft/gpl.html

use IO::File;
use Getopt::Std;

sub PPTP_Header;
sub WriteReport;

# Parse command line
$arg_err = getopts('f:knh');

if ($arg_err == 0 || $opt_h == 1) {
    print "Usage: pptplog [-f filename] [-kh]\n";
    print "-f     Specifies log file to read (default is
/var/log/messages)\n";
    print "-k     Keep temporary files in /tmp (default is to delete these
files)\n";
    print "-n     Do not send any mail (default is to send to root)\n";
    print "-h     Show this message\n";
    exit;
}

if ($opt_f ne "") {
    $messages_file = $opt_f;
}
else {
    $messages_file = "/var/log/messages";
}

# Input file
open(MESSAGES, $messages_file) or die "Can't open messages file:$!\n";

# Output files for failed logins, insecure logins, and valid logins
$failed_file = "/tmp/vpn_failed";
$insecure_file = "/tmp/vpn_insecure";
$valid_file = "/tmp/vpn_valid";
$report_file = "/tmp/vpn_report";

open(FAILED, "> " . $failed_file) or die "Can't open $! for output\n";
open(INSECURE, "> " . $insecure_file) or die "Can't open $! for output\n";
open(VALID, "> " . $valid_file) or die "Can't open $! for output\n";

print FAILED "-" x 10 . " Failed login attempts " . "-" x 10 . "\n";
print INSECURE "-" x 10 . " Insecure logins " . "-" x 10 . "\n";
print VALID "-" x 10 . " Valid logins " . "-" x 10 . "\n";

while ($line = <MESSAGES>) {
    if ($line =~ /pptpd\[\d+\]:\sCTRL:.*control connection started/) {
	PPTP_Header;		# Process the start of the PPTP connection
    }
    elsif ($line =~ /pppd\[(\d+)\]:\s.*peer authentication/) {
	# User authentication
	$ppp_id = $1;
	$_ = $line;
	if (/\d+\]: ([\w\-]+)/) {
	    $chaptype{$ppp_id} = $1;
	}
	if (/authentication (\w+) for.* (\w+)$/) {
	    $username{$ppp_id} = $2;
	    if ($1 eq "failed") {
		$loginok{$ppp_id} = 0;
	    }
	    else {
		$loginok{$ppp_id} = 1;
	    }
	}
    }
    elsif ($line =~ /pppd\[(\d+)\]:\s.*compression/i) {
	$ppp_id = $1;
	$_ = $line;
	if (/Compression disabled/) {
	    $crypt{$ppp_id} = "NONE";
	}
	elsif (/(MPPE \d+ bit)/) {
	    $crypt{$ppp_id} = $1;
	}
	elsif (/Deflate.*compression enabled/) {
	    $crypt{$ppp_id} = "Deflate";
	}
    }
    elsif ($line =~ /pptpd\[(\d+)\]:\sCTRL: PTY read or GRE write failed/) {
	$errors{$1} .= "r/w ";
    }
    elsif ($line =~ /pptpd\[(\d+)\]:\sCTRL: Session timed out, ending call/)
{
	$errors{$1} .= "session-timeout ";
    }
    elsif ($line =~ /pptpd\[(\d+)\]:\sLCP: timeout sending Config/) {
	$errors{$1} .= "cfg-req-timeout ";
    }
    elsif ($line =~ /pptpd\[(\d+)\]:\sCTRL: EOF or bad error reading ctrl/)
{
	$pptp_id = $1;
	if (!($errors{$pptp_id} =~ /ctrl-packet-bad/)) {  # Only log one bad ctrl
packet
	    $errors{$pptp_id} .= "ctrl-packet-bad ";
	}
    }
    elsif ($line =~ /pptpd\[(\d+)\]:\sGRE: Discarding/) {
	$pptp_id = $1;
	if (!($errors{$pptp_id} =~ /GRE-discard/)) {  # Only log one GRE error
	    $errors{$pptp_id} .= "GRE-discard ";
	}
    }
    elsif ($line =~ /pppd\[(\d+)\]: Connect time (\d+\.\d+) min/) {
	$time{$1} = $2;
    }
    elsif ($line =~ /pppd\[(\d+)\]: Sent (\d+) by.* (\d+) by/) {
	$sent{$1} = $2;
	$rcvd{$1} = $3;
    }
    elsif ($line =~ /pppd\[(\d+)\]: Exit/) {
	$ppp_id = $1;
	WriteReport;
    }
}

# Write out blank lines for readability
print FAILED "\n";
print INSECURE "\n";

close (FAILED);
close (INSECURE);
close (VALID);
`cat $failed_file $insecure_file $valid_file > $report_file`;

if ($opt_n != 1) {
    `mail -s "VPN Report" root < $report_file`;
}

if ($opt_k != 1) {
    `rm $failed_file $insecure_file $valid_file $report_file`;
}


#########################################################################
# Process the start of the PPTP connection.  Hash tables are used to track
# information because it's possible to having overlapping sessions with
# starts & ends interleaved.
sub PPTP_Header {
    $_ = $line;
    if (/^(\w{3}\s+\d+\s+..:..:..).*pptpd\[(\d+)\]:\sCTRL: Client
(\d+.\d+.\d+.\d+)/)
    {
	$date = $1;
	$pptp_id = $2;
	$ip = $3;
    }

    # Find the line that launches pppd so we can track that PID
    while ($line = <MESSAGES>) {
	if ($line =~ /pppd\[(\d+)\]:\s.*started by/) {
	    $ppp_id = $1;
	    last;
	}
    }

    # Track values that are keyed to the PID of pptpd.  This is needed
because
    # it's possible to have interleaving start/end pptpd connection messages
    $ppp_id_hash{$pptp_id} = $ppp_id;  # The PID of the pppd that was
launched by pptpd
    $pptp_id_hash{$ppp_id} = $pptp_id; # The PID of the pptp that launched
this pppd
    $date_hash{$pptp_id} = $date;
    $ip_hash{$pptp_id} = $ip;
}

#########################################################################
# Finished with one session, so write to the appropriate report file
sub WriteReport {
    $pptp_id = $pptp_id_hash{$ppp_id};

    # Fixup null strings
    if ($crypt{$ppp_id} eq "") {
	$crypt{$ppp_id} = "Empty";
    }
    if ($errors{$pptp_id} eq "") {
	$errors{$pptp_id} = "none";
    }

    # Failed login?
    if ($loginok{$ppp_id} == 0)

	print FAILED "$date_hash{$pptp_id} USER:$username{$ppp_id}
IP:$ip_hash{$pptp_id}\n";
    }
    # Insecure login?
    elsif (($chaptype{$ppp_id} ne "MSCHAP-v2") ||
	   (($crypt{$ppp_id} ne "MPPE 128 bit") && ($crypt{$ppp_id} ne "MPPE 40
bit"))) {
	print INSECURE "$date_hash{$pptp_id} USER:$username{$ppp_id}
IP:$ip_hash{$pptp_id} ";
	print INSECURE "AUTH:$chaptype{$ppp_id} CRYPT:$crypt{$ppp_id} ";
	print INSECURE "TIME:$time{$ppp_id} SENT:$sent{$ppp_id} RCVD:$rcvd{$ppp_id}
ERRS:$errors{$pptp_id} \n";
    }
    # Valid login
    else {
	print VALID "$date_hash{$pptp_id} USER:$username{$ppp_id}
IP:$ip_hash{$pptp_id} ";
	print VALID "AUTH:$chaptype{$ppp_id} CRYPT:$crypt{$ppp_id} ";
	print VALID "TIME:$time{$ppp_id} SENT:$sent{$ppp_id} RCVD:$rcvd{$ppp_id}
ERRS:$errors{$pptp_id} \n";
    }
}




More information about the pptp-server mailing list