849 lines
32 KiB
Python
849 lines
32 KiB
Python
|
|
|
|
#!/usr/bin/python -tt
|
|
"""
|
|
Description will eventually go here.
|
|
"""
|
|
##
|
|
# Copyright (C) 2003 by Duke University
|
|
#
|
|
# 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.
|
|
#
|
|
# $Id$
|
|
#
|
|
# @Author Konstantin Ryabitsev <icon@linux.duke.edu>
|
|
# @version $Date$
|
|
#
|
|
|
|
import sys
|
|
import re
|
|
import time
|
|
import os
|
|
import sqlite3 as sqlite
|
|
sys.path.insert(0, '../py/')
|
|
from epylog import InternalModule
|
|
|
|
|
|
def executeSQL(cursor, query, params=None):
|
|
"""
|
|
Execute a python 2.5 (sqlite3) style query.
|
|
|
|
@param cursor: A sqlite cursor
|
|
@param query: The query to execute
|
|
@param params: An optional list of parameters to the query
|
|
"""
|
|
if params is None:
|
|
return cursor.execute(query)
|
|
|
|
return cursor.execute(query, params)
|
|
|
|
class logins_mod(InternalModule):
|
|
def __init__(self, opts, logger):
|
|
InternalModule.__init__(self)
|
|
self.logger = logger
|
|
self.opts = opts
|
|
rc = re.compile
|
|
|
|
self.ignore = 0
|
|
self.open = 1
|
|
self.failure = 2
|
|
self.root_open = 11
|
|
self.root_failure = 12
|
|
self.pam_ignore = []
|
|
self.xinetd_ignore = []
|
|
self.logins_db = opts.get('loginsdb_path', '/var/lib/epylog/logins_db.sqlite') # where to keep the loginsdb
|
|
self.time_fuzz = int(opts.get('time_fuzz', 60)) # how much to fuzz the time in minutes (default 60m)
|
|
remove_older_than = int(opts.get('remove_older_than', 14)) # time in days to start remove from the db
|
|
self.oldest_to_keep = time.time() - (remove_older_than*86400)
|
|
if remove_older_than == '0': # if it is zero then don't delete any, ever - hah your funeral
|
|
self.oldest_to_keep = None
|
|
|
|
ig_users = opts.get('ignore_users', '')
|
|
ig_users.replace(',',' ')
|
|
self.ignore_users = ig_users.split(' ')
|
|
|
|
self.db_cx = None
|
|
|
|
##
|
|
# PAM reports
|
|
#
|
|
pam_map = {
|
|
rc('\(pam_unix\)\S*:.*authentication\s*failure'): self.pam_failure,
|
|
rc('\(pam_unix\)\S*:\ssession\sopened\sfor'): self.pam_open,
|
|
rc('\(pam_unix\)\S*:\sbad\susername'): self.pam_baduser,
|
|
rc('\(pam_unix\)\S*:\sauth\scould\snot'): self.pam_chelper_failure,
|
|
rc('pam_krb5\S*:\s\S+\ssucceeds\sfor'): self.pam_krb5_open,
|
|
rc('pam_krb5\S*:\s\S+\sfails\sfor'): self.pam_krb5_failure
|
|
}
|
|
##
|
|
# XINETD reports
|
|
#
|
|
xinetd_map = {
|
|
rc('xinetd\S*: START:'): self.xinetd_start
|
|
}
|
|
##
|
|
# SSH reports
|
|
#
|
|
sshd_map = {
|
|
rc('sshd\[\S*: Accepted'): self.sshd_open,
|
|
rc('sshd\[\S*: Failed'): self.sshd_failure
|
|
}
|
|
##
|
|
# IMAPD and IPOP3D
|
|
#
|
|
uw_imap_map = {
|
|
rc('imapd\[\S*: Login\sfail'): self.uw_imap_failure,
|
|
rc('imapd\[\S*: Authenticated\suser'): self.uw_imap_open,
|
|
rc('imapd\[\S*: Login\suser'): self.uw_imap_open,
|
|
rc('ipop3d\[\S*: Login\sfail'): self.uw_imap_failure,
|
|
rc('ipop3d\[\S*: Login\suser'): self.uw_imap_open,
|
|
rc('ipop3d\[\S*: Auth\suser'): self.uw_imap_open
|
|
}
|
|
##
|
|
# IMP
|
|
#
|
|
imp_map = {
|
|
rc('IMP\[\S*: Login'): self.imp2_open,
|
|
rc('IMP\[\S*: FAILED'): self.imp2_failure,
|
|
rc('HORDE\[\S*\s*\[imp\] Login'): self.imp3_open,
|
|
rc('HORDE\[\S*\s*\[imp\] FAILED'): self.imp3_failure
|
|
}
|
|
##
|
|
# DOVECOT
|
|
#
|
|
dovecot_map = {
|
|
rc('imap-login:\sLogin:\s'): self.dovecot_open,
|
|
rc('imap-login:\sAborted\slogin\s'): self.dovecot_failure
|
|
}
|
|
##
|
|
# Courier-IMAP
|
|
#
|
|
courier_map = {
|
|
rc('\sLOGIN,\suser=\S+,\sip=\[\S+\]'): self.courier_open,
|
|
rc('\sLOGIN FAILED,\sip=\[\S+\]'): self.courier_failure
|
|
}
|
|
##
|
|
# Cyrus-IMAP
|
|
#
|
|
cyrus_map = {
|
|
rc('imapd\[\S*: login:'): self.cyrus_open,
|
|
rc('pop3d\[\S*: login:'): self.cyrus_open,
|
|
rc('imapd\[\S*: badlogin:'): self.cyrus_failure,
|
|
rc('pop3d\[\S*: badlogin:'): self.cyrus_failure
|
|
}
|
|
##
|
|
# Qpopper
|
|
#
|
|
qpopper_map = {
|
|
rc('apop\[\S*:\s\S+\sat\s.*\s\(\S*\):\s-ERR\s\[AUTH\]'): self.qpopper_failure,
|
|
rc('apop\[\S*:\s\S+\sat\s.*\s\(\S*\):\s-ERR\s\[IN-USE\]'): self.qpopper_failure,
|
|
rc('apop\[\S*:\s\(\S*\)\sPOP\slogin'): self.qpopper_open
|
|
}
|
|
|
|
##
|
|
# ProFTPD
|
|
#
|
|
proftpd_map = {
|
|
rc('proftpd\[\S*:.*USER.*Login successful'): self.proftpd_open,
|
|
rc('proftpd\[\S*:.*no such user found'): self.proftpd_failure,
|
|
rc('proftpd\[\S*:.*Login failed'): self.proftpd_failure
|
|
}
|
|
|
|
regex_map = {}
|
|
if opts.get('enable_pam', "1") != "0": regex_map.update(pam_map)
|
|
if opts.get('enable_xinetd', "1") != "0": regex_map.update(xinetd_map)
|
|
if opts.get('enable_sshd', "1") != "0":
|
|
regex_map.update(sshd_map)
|
|
self.pam_ignore.append('sshd')
|
|
if opts.get('enable_uw_imap', "0") != "0":
|
|
regex_map.update(uw_imap_map)
|
|
self.xinetd_ignore.append('imaps')
|
|
if opts.get('enable_imp', "0") != "0": regex_map.update(imp_map)
|
|
if opts.get('enable_dovecot',"0") != "0": regex_map.update(dovecot_map)
|
|
if opts.get('enable_courier',"0") != "0": regex_map.update(courier_map)
|
|
if opts.get('enable_cyrus', "0") != "0": regex_map.update(cyrus_map)
|
|
if opts.get('enable_qpopper',"0") != "0": regex_map.update(qpopper_map)
|
|
if opts.get('enable_proftpd',"0") != "0":
|
|
regex_map.update(proftpd_map)
|
|
self.pam_ignore.append('ftp')
|
|
self.xinetd_ignore.append('ftp')
|
|
|
|
self.safe_domains = []
|
|
safe_domains = opts.get('safe_domains', '.*')
|
|
for domain in safe_domains.split(','):
|
|
domain = domain.strip()
|
|
if domain:
|
|
try:
|
|
domain_re = rc(domain)
|
|
self.safe_domains.append(domain_re)
|
|
except:
|
|
logger.put(0, 'Error compiling domain regex: %s' % domain)
|
|
logger.put(0, 'Check config for Logins module!')
|
|
|
|
self.regex_map = regex_map
|
|
|
|
self.pam_service_re = rc('(\S+)\(pam_unix\)')
|
|
self.pam_failure_re = rc('.*\slogname=(\S*).*\srhost=(\S*)')
|
|
self.pam_failure_user_re = rc('\suser=(\S*)')
|
|
self.pam_open_re = rc('.*for user (\S+) by\s(\S*)\s*\(uid=(\S+)\)')
|
|
self.pam_failure_more_re = rc('(\S+)\smore\sauthentication\sfailures')
|
|
self.pam_baduser_re = rc('\sbad\susername\s\[(.*)\]')
|
|
self.pam_chelper_re = rc('password\sfor\s\[(.*)\]')
|
|
self.pam_krb5_re = rc("^(\S+?)\[*\d*\]*:\spam_krb5\S*:\sauth.*\sfor\s`(\S+)'")
|
|
self.xinetd_start_re = rc('START:\s*(\S*)\s')
|
|
self.sshd_open_ruser_re = rc('Accepted\s(\S*)\sfor\s(\S*)\sfrom\s(\S*)\sport\s\d*\sruser\s(\S*)\s*(\S*)')
|
|
self.sshd_open_re = rc('Accepted\s(\S*)\sfor\s(\S*)\sfrom\s(\S*)\sport\s\d+\s*(\S*)')
|
|
self.sshd_fail_re = rc('Failed\s(\S*)\sfor.*\s(\S+)\sfrom\s(\S*)\sport\s\d*\s*(\S*)')
|
|
self.uw_imap_fail_re = rc('auth=(.*)\shost=.*\[(\S*)\]')
|
|
self.uw_imap_open_re = rc('user=(.*)\shost=.*\[(\S*)\]')
|
|
self.uw_imap_service_re = rc('^(\S*)\[\d*\]:')
|
|
self.dovecot_open_re = rc('Login:\s(\S+)\s\[(\S+)\]')
|
|
self.dovecot_failure_re = rc('Aborted\slogin\s\[(\S+)\]')
|
|
self.courier_open_re = rc('^(\S+?):.*\suser=(\S+),\sip=\[(\S+)\]')
|
|
self.courier_failure_re = rc('^(\S+?):.*,\sip=\[(\S+)\]')
|
|
self.imp2_open_re = rc('Login\s(\S*)\sto\s(\S*):\S*\sas\s(\S*)')
|
|
self.imp2_fail_re = rc('FAILED\s(\S*)\sto\s(\S*):\S*\sas\s(\S*)')
|
|
self.imp3_open_re = rc('success\sfor\s(\S*)\s\[(\S*)\]\sto\s\{(\S*):')
|
|
self.imp3_fail_re = rc('LOGIN\s(\S*)\sto\s(\S*):\S*\sas\s(\S*)')
|
|
self.proftpd_open_re = rc('proftpd\[\S*:.*\[(\S+)\].*USER\s(.*):\sLogin\ssuccessful')
|
|
self.proftpd_failure_re = rc('proftpd\[\S*:.*\[(\S+)\].*USER\s([^:\s]*)')
|
|
self.qpopper_open_re = rc('user "(.*)" at \(.*\)\s(\S*)')
|
|
self.qpopper_fail_re = rc(':\s(.*)\sat\s(\S*)')
|
|
self.cyrus_open_re = rc('login:.*\[(\S*)\]\s(\S*)\s')
|
|
self.cyrus_fail_re = rc('badlogin:.*\[(\S*)\]\s\S\s(\S*)\sSASL')
|
|
self.cyrus_service_re = rc('^(\S*)\[\d*\]:')
|
|
|
|
self.sshd_methods = {'password': 'pw',
|
|
'publickey': 'pk',
|
|
'rhosts-rsa': 'rsa',
|
|
'rsa': 'rsa',
|
|
'hostbased': 'host',
|
|
'none': 'none'}
|
|
|
|
self.report_wrap = '<table width="100%%" rules="cols" cellpadding="2">%s</table>'
|
|
self.subreport_wrap = '<tr><th align="left" colspan="3"><h3>%s</h3></th></tr>\n%s\n'
|
|
|
|
self.root_failures_title = '<font color="red">ROOT FAILURES</font>'
|
|
self.root_logins_title = '<font color="blue">ROOT Logins</font>'
|
|
self.user_failures_title = '<font color="red">User Failures</font>'
|
|
self.user_logins_title = '<font color="blue">User Logins</font>'
|
|
|
|
self.untrusted_host = '%(system)s::<font color="red">%(rhost)s</font>'
|
|
|
|
self.flip = ' bgcolor="#dddddd"'
|
|
|
|
self.line_rep = '<tr%s><td valign="top" width="15%%">%s</td><td valign="top" width="15%%">%s</td><td width="70%%">%s</td></tr>\n'
|
|
|
|
##
|
|
# LINE MATCHING ROUTINES
|
|
#
|
|
def general_ignore(self, linemap):
|
|
restuple = (self.ignore, None, None, None)
|
|
return {restuple: 1}
|
|
|
|
def pam_failure(self, linemap):
|
|
action = self.failure
|
|
self.logger.put(5, 'pam_failure invoked')
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = self._get_pam_service(message)
|
|
mo = self.pam_failure_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd pam failure string: %s' % message)
|
|
return None
|
|
byuser, rhost = mo.groups()
|
|
mo = self.pam_failure_user_re.search(message)
|
|
if mo: user = mo.group(1)
|
|
else: user = 'unknown'
|
|
if ((service == 'xscreensaver' and user == 'root')
|
|
or service == 'sshd' or service == 'imap'):
|
|
##
|
|
# xscreensaver will always fail as root.
|
|
# SSHD is better handled by sshd part itself.
|
|
# Imap failures are caught by imap routines.
|
|
# Ignore these.
|
|
#
|
|
result = self.general_ignore(linemap)
|
|
return result
|
|
mo = self.pam_failure_more_re.search(message)
|
|
if mo: mult += int(mo.group(1))
|
|
restuple = self._mk_restuple(action, system, service, user,
|
|
byuser, rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def pam_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = self._get_pam_service(message)
|
|
if service in self.pam_ignore:
|
|
##
|
|
# the service will do a much better job.
|
|
#
|
|
result = self.general_ignore(linemap)
|
|
return result
|
|
mo = self.pam_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd pam open string: %s' % message)
|
|
return None
|
|
user, byuser, byuid = mo.groups()
|
|
if byuser == '': byuser = self.getuname(int(byuid))
|
|
restuple = self._mk_restuple(action, system, service, user, byuser, '', linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def pam_baduser(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.pam_baduser_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd pam bad user string: %s' % message)
|
|
return None
|
|
user = mo.group(1)
|
|
service = self._get_pam_service(message)
|
|
restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def pam_chelper_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.pam_chelper_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd pam console helper string: %s' % message)
|
|
return None
|
|
user = mo.group(1)
|
|
service = self._get_pam_service(message)
|
|
restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def pam_krb5_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.pam_krb5_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd pam_krb5 succeeds line: %s' % message)
|
|
return None
|
|
service = mo.group(1)
|
|
user = mo.group(2)
|
|
if service == 'sshd':
|
|
##
|
|
# sshd_open will do a much better job.
|
|
#
|
|
result = self.general_ignore(linemap)
|
|
return result
|
|
restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def pam_krb5_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.pam_krb5_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd pam_krb5 failure line: %s' % message)
|
|
return None
|
|
service = mo.group(1)
|
|
user = mo.group(2)
|
|
if ((service == 'xscreensaver' and user == 'root')
|
|
or service == 'sshd' or service == 'imap'):
|
|
##
|
|
# xscreensaver will always fail as root.
|
|
# SSHD is better handled by sshd part itself.
|
|
# Imap failures are caught by imap routines.
|
|
# Ignore these.
|
|
#
|
|
result = self.general_ignore(linemap)
|
|
return result
|
|
restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def xinetd_start(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.xinetd_start_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd xinetd start string: %s' % message)
|
|
return None
|
|
service = mo.group(1)
|
|
if service in self.xinetd_ignore:
|
|
##
|
|
# the service will do a much better job.
|
|
#
|
|
result = self.general_ignore(linemap)
|
|
return result
|
|
restuple = self._mk_restuple(action, system, service, '', '', '', linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def sshd_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
ruser = ''
|
|
mo1 = self.sshd_open_ruser_re.search(message)
|
|
mo2 = self.sshd_open_re.search(message)
|
|
if mo1: method, user, rhost, ruser, service = mo1.groups()
|
|
elif mo2: method, user, rhost, service = mo2.groups()
|
|
else:
|
|
self.logger.put(3, 'Odd sshd open string: %s' % message)
|
|
return None
|
|
method = self.sshd_methods.get(method, '??')
|
|
rhost = self.gethost(rhost)
|
|
if not service: service = 'ssh1'
|
|
service = '%s(%s)' % (service, method)
|
|
restuple = self._mk_restuple(action, system, service, user,
|
|
ruser, rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def sshd_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.sshd_fail_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd sshd FAILURE string: %s' % message)
|
|
return None
|
|
method, user, rhost, service = mo.groups()
|
|
method = self.sshd_methods.get(method, '??')
|
|
rhost = self.gethost(rhost)
|
|
if not service: service = 'ssh1'
|
|
service = '%s(%s)' % (service, method)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def uw_imap_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = self._get_uw_imap_service(message)
|
|
service = '%s(uw)' % service
|
|
mo = self.uw_imap_fail_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd imap FAILURE string: %s' % message)
|
|
return None
|
|
user, rhost = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def uw_imap_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = self._get_uw_imap_service(message)
|
|
service = '%s(uw)' % service
|
|
mo = self.uw_imap_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd imap open string: %s' % message)
|
|
return None
|
|
user, rhost = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def dovecot_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = 'imap(dc)'
|
|
mo = self.dovecot_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd dovecot OPEN string: %s' % message)
|
|
return None
|
|
user, rhost = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def dovecot_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = 'imap(dc)'
|
|
mo = self.dovecot_failure_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd dovecot FAILURE string: %s' % message)
|
|
return None
|
|
rhost = mo.group(1)
|
|
rhost = self.gethost(rhost)
|
|
user = 'unknown'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def courier_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.courier_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd courier OPEN string: %s' % message)
|
|
return None
|
|
service, user, rhost = mo.groups()
|
|
service = '%s(cr)' % service
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def courier_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.courier_failure_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd courier FAILURE string: %s' % message)
|
|
return None
|
|
service, rhost = mo.groups()
|
|
service = '%s(cr)' % service
|
|
rhost = self.gethost(rhost)
|
|
user = 'unknown'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def proftpd_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.proftpd_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd ProFTPD OPEN string: %s' % message)
|
|
return None
|
|
service = 'ftp(pro)'
|
|
rhost, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def proftpd_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.proftpd_failure_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd ProFTPD FAILURE string: %s' % message)
|
|
return None
|
|
service = 'ftp(pro)'
|
|
rhost, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def imp2_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.imp2_fail_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd IMP failure string: %s' % message)
|
|
return None
|
|
rhost, system, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
service = 'IMP2'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def imp2_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.imp2_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd IMP open string: %s' % message)
|
|
return None
|
|
rhost, system, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
service = 'IMP2'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def imp3_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.imp3_fail_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd IMP failure string: %s' % message)
|
|
return None
|
|
rhost, system, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
service = 'IMP3'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def imp3_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.imp3_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd IMP open string: %s' % message)
|
|
return None
|
|
user, rhost, system = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
service = 'IMP3'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def cyrus_failure(self,linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = self._get_cyrus_service(message)
|
|
mo = self.cyrus_fail_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd cyrus FAILURE string: %s' % message)
|
|
return None
|
|
rhost, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def cyrus_open(self,linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
service = self._get_cyrus_service(message)
|
|
mo = self.cyrus_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd cyrus open string: %s' % message)
|
|
return None
|
|
rhost, user = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def qpopper_failure(self, linemap):
|
|
action = self.failure
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.qpopper_fail_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd qpopper FAILURE string: %s' % message)
|
|
return None
|
|
user, rhost = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
service = 'qpopper'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
|
|
def qpopper_open(self, linemap):
|
|
action = self.open
|
|
system, message, mult = self.get_smm(linemap)
|
|
mo = self.qpopper_open_re.search(message)
|
|
if not mo:
|
|
self.logger.put(3, 'Odd qpopper open string: %s' % message)
|
|
return None
|
|
user, rhost = mo.groups()
|
|
rhost = self.gethost(rhost)
|
|
service = 'qpopper'
|
|
restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
|
|
return {restuple: mult}
|
|
##
|
|
# HELPER METHODS
|
|
#
|
|
def _mk_restuple(self, action, system, service, user, byuser, rhost, stamp):
|
|
if user == '': user = 'unknown'
|
|
if user == 'root' or user == 'ROOT':
|
|
action += 10
|
|
remote = self._mk_userat(byuser, rhost)
|
|
restuple = (action, system, service, remote, stamp)
|
|
else:
|
|
if rhost:
|
|
match = 0
|
|
for domain_re in self.safe_domains:
|
|
if domain_re.search(rhost):
|
|
match = 1
|
|
break
|
|
if not match:
|
|
tmp = {'system': system, 'rhost': rhost}
|
|
system = self.untrusted_host % tmp
|
|
restuple = (action, user, service, system, stamp)
|
|
return restuple
|
|
|
|
def _mk_dots(self, str, lim):
|
|
if len(str) > lim:
|
|
start = -(lim-2)
|
|
str = '..' + str[start:]
|
|
return str
|
|
|
|
def _get_pam_service(self, str):
|
|
service = 'unknown'
|
|
mo = self.pam_service_re.search(str)
|
|
if mo: service = mo.group(1)
|
|
return service
|
|
|
|
def _get_uw_imap_service(self, str):
|
|
service = 'unknown'
|
|
mo = self.uw_imap_service_re.search(str)
|
|
if mo: service = mo.group(1)
|
|
return service
|
|
|
|
def _mk_userat(self, user, host):
|
|
if user and host: userat = '%s@%s' % (user, host)
|
|
elif user: userat = user
|
|
elif host: userat = '@%s' % host
|
|
else: userat = 'unknown'
|
|
return userat
|
|
|
|
def _get_cyrus_service(self, str):
|
|
service = 'unknown'
|
|
mo = self.cyrus_service_re.search(str)
|
|
if mo: service = mo.group(1)
|
|
return service
|
|
|
|
|
|
|
|
def _check_for_login(self, username, service, hostname, tid):
|
|
# check if we have a login which matches in the db,
|
|
|
|
if not os.path.exists(self.logins_db):
|
|
return False
|
|
|
|
p = (username, service, hostname, self.last_entry)
|
|
q = "select * from logins where username=? and service=? and host=? and pkey <= ?"
|
|
if not self.db_cx:
|
|
self._db_cx()
|
|
|
|
cur = self.db_cx.cursor()
|
|
ob = executeSQL(cur, q, p)
|
|
for i in ob:
|
|
# if we get any matches if they match within the fuzzed time then
|
|
# don't show it
|
|
if abs(tid - i[7]) <= self.time_fuzz*60:
|
|
return True
|
|
return False
|
|
|
|
def _add_login(self, username, action, hostname, timestamp, service, u_from=None):
|
|
if not self.db_cx:
|
|
self._db_cx()
|
|
|
|
cur = self.db_cx.cursor()
|
|
t_st = time.localtime(int(timestamp))
|
|
time_in_day = int(t_st[3]*60) + int(t_st[4])
|
|
q = "insert into logins values (NULL, ?, ?, ?, ?, ?, ?, ?)"
|
|
p = (action, username, hostname, u_from, service, timestamp, time_in_day)
|
|
cur.execute(q, p)
|
|
|
|
|
|
def _db_cx(self):
|
|
if not os.path.exists(self.logins_db):
|
|
self.db_cx = self._setup_logins_db()
|
|
else:
|
|
self.db_cx = sqlite.Connection(self.logins_db)
|
|
|
|
q = "select max(pkey) from logins"
|
|
last_e = executeSQL(self.db_cx.cursor(), q)
|
|
val = last_e.fetchone()[0]
|
|
if not val:
|
|
self.last_entry = 0
|
|
else:
|
|
self.last_entry = val
|
|
|
|
def _setup_logins_db(self):
|
|
schema = [
|
|
"""PRAGMA synchronous="OFF";""",
|
|
"""CREATE TABLE logins ( pkey INTEGER PRIMARY KEY, action INTEGER,
|
|
username TEXT, host TEXT, u_from TEXT, service TEXT,
|
|
stamp INTEGER, time_in_day INTEGER);""",
|
|
]
|
|
|
|
cx = sqlite.Connection(self.logins_db)
|
|
cursor = cx.cursor()
|
|
for cmd in schema:
|
|
executeSQL(cursor, cmd)
|
|
|
|
return cx
|
|
|
|
##
|
|
# FINALIZE!!
|
|
#
|
|
def finalize(self, rs):
|
|
logger = self.logger
|
|
##
|
|
# Prepare report
|
|
#
|
|
report = ''
|
|
rep = {}
|
|
|
|
# FIXME
|
|
# go through each item in the rs
|
|
# feed them into the db
|
|
# pull back from the db all the info you need for the report
|
|
# simplifies a lot of this code
|
|
|
|
# chuck it all into the db
|
|
for (rt,count) in rs.items():
|
|
if rt[0] in (self.root_failure, self.root_open):
|
|
(action, host, service, remote, stamp) = rt
|
|
user = 'root'
|
|
elif rt[0] in (self.open, self.failure):
|
|
(action, user, service, host, stamp) = rt
|
|
remote = 'NULL'
|
|
else:
|
|
continue
|
|
if user in self.ignore_users:
|
|
continue
|
|
for num in range(0, count):
|
|
self._add_login(user, action, host, stamp, service, remote)
|
|
self.db_cx.commit()
|
|
|
|
#return "lalallala"
|
|
|
|
for action in [self.root_failure, self.root_open,
|
|
self.failure, self.open]:
|
|
rep[action] = ''
|
|
per_user = {}
|
|
flipper = ''
|
|
q = """select distinct username, service, host from logins where action = ? and pkey > ?"""
|
|
p = (action, self.last_entry)
|
|
act_tuple = [(i[0],i[1], i[2]) for i in executeSQL(self.db_cx.cursor(), q, p)]
|
|
|
|
for entry in act_tuple:
|
|
username = entry[0]
|
|
if username not in per_user:
|
|
per_user[username] = {}
|
|
service = entry[1]
|
|
if service not in per_user[username]:
|
|
per_user[username][service] = []
|
|
hn = entry[2]
|
|
q = """select time_in_day from logins where username = ? and host = ? and service = ? and action = ? and pkey > ?"""
|
|
p = (username, hn, service, action, self.last_entry)
|
|
|
|
this_logins_times = [row[0] for row in executeSQL(self.db_cx.cursor(), q, p)]
|
|
count = 0
|
|
for t in this_logins_times:
|
|
if not self._check_for_login(username, service, hn, t):
|
|
# DEBUG print 'new login %s %s %s %s' % (username, service, hn, t)
|
|
count += 1
|
|
|
|
if count:
|
|
per_user[username][service].append('%s(%d)' % (hn, count))
|
|
|
|
blank = 0
|
|
for username in sorted(per_user):
|
|
if flipper: flipper = ''
|
|
else: flipper = self.flip
|
|
for (svc,reps) in per_user[username].items():
|
|
if blank: key = ' '
|
|
else: blank = 1
|
|
if reps:
|
|
rep[action] += self.line_rep % (flipper, username,
|
|
svc, ', '.join(reps))
|
|
|
|
if rep[self.root_failure]:
|
|
report += self.subreport_wrap % (self.root_failures_title,
|
|
rep[self.root_failure])
|
|
if rep[self.root_open]:
|
|
report += self.subreport_wrap % (self.root_logins_title,
|
|
rep[self.root_open])
|
|
if rep[self.failure]:
|
|
report += self.subreport_wrap % (self.user_failures_title,
|
|
rep[self.failure])
|
|
if rep[self.open]:
|
|
report += self.subreport_wrap % (self.user_logins_title,
|
|
rep[self.open])
|
|
|
|
report = self.report_wrap % report
|
|
|
|
if self.oldest_to_keep:
|
|
q = """delete from logins where stamp < ?"""
|
|
p = (self.oldest_to_keep,)
|
|
executeSQL(self.db_cx.cursor(), q, p)
|
|
self.db_cx.commit()
|
|
|
|
return report
|
|
|
|
if __name__ == '__main__':
|
|
from epylog.helpers import ModuleTest
|
|
ModuleTest(logins_mod, sys.argv)
|
|
|