#!/usr/bin/python

#  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#  Copyright (c) 2007,2008 Ezequiel Vera

#------------------------------------------------------------------------------
#  Nombre: auth2db.py
#  Autor: Ezequiel Vera (ezequielvera@yahoo.com.ar)
#  Ult. Modificacion: 30/07/2008
#  Description: Parsea el archivo auth.log en busca de logins (smb,ssh,su,login,gdm)
#               y lo pasa a una base de datos mysql que permite listar y ordenar 
#               los resultados.
#------------------------------------------------------------------------------


__author__ = "Ezequiel Vera"
__version__ = "0.2.5"
__date__ = "2008-07-30"
__copyright__ = "Copyright (c) 2007,2008 Ezequiel Vera"
__license__ = "GPL"


import MySQLdb
import os
import sys
import string
import time
import datetime
import traceback
import socket
import re
import smtplib
import codecs

sys.path.insert(1,"/usr/share/auth2db/modules/")

from configobj import ConfigObj

#CONFIG_AUTH_PATH = "/var/log/"
CONFIG_PATH = "/etc/auth2db/"
#CONFIG_PATH_FLAG = CONFIG_PATH+"flag.d/"
CONFIG_PATH_FLAG = "/var/lib/auth2db/flag.d/"
#CONFIG_PATH_TMP = "/tmp/"
CONFIG_PATH_TMP = "/var/lib/auth2db/tmp/"

config = ConfigObj(CONFIG_PATH+'auth2db.conf')
config_filters = ConfigObj(CONFIG_PATH+"filters.conf")

# Carga en las variables el archivo config.dat
CONFIG_HOST = config['CONFIG_HOST']
CONFIG_DB = config['CONFIG_DB']
CONFIG_USER = config['CONFIG_USER']
CONFIG_PASS = config['CONFIG_PASS']	

UPDATE_IP_SSHD = config['UPDATE_IP_SSHD']	

today = datetime.date.today()

# VALIDAR CONECCION MySQLdb
try:
	conn = MySQLdb.connect (host = CONFIG_HOST,
							user = CONFIG_USER,
							passwd = CONFIG_PASS,
							db = CONFIG_DB)
except MySQLdb.Error, e:
	print "Error %d: %s" % (e.args[0], e.args[1])
	sys.exit (1)
	
	

def mesreplace(s):
	mes_string = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')
	mes_number = ('01','02','03','04','05','06','07','08','09','10','11','12')
	
	for a, b in zip(mes_string, mes_number):
		s = s.replace(a, b)
	return s


def regexAction(tipo,archivolog,rawstr,section,action):
	
	os.system("grep -Ei '("+action+")' "+archivolog+" > "+CONFIG_PATH_TMP+"auth2db_"+section+'.log')
	os.system("sed -e 's/[`]//g' -i "+CONFIG_PATH_TMP+"auth2db_"+section+'.log')
	os.system('''sed -e "s/[']//g" -i '''+CONFIG_PATH_TMP+"auth2db_"+section+'.log')
	''' "'" '''
			 
	obj_flag.section = section
	obj_flag.flaginiciar()
	
	desde = obj_flag.flagcheck()
	
	if desde == '':
		desde = 0
	else:
		desde = desde + 1

	
	server = socket.gethostname()
			
	# connect
	db = MySQLdb.connect(host=CONFIG_HOST, user=CONFIG_USER, passwd=CONFIG_PASS, db=CONFIG_DB)
	# create a cursor
	inserta = db.cursor()
	
	compile_obj = re.compile(rawstr,  re.IGNORECASE| re.MULTILINE)
	
	
	# carga el archivo para revisar con regex
	f = codecs.open( CONFIG_PATH_TMP+"auth2db_"+section+'.log', "r", "iso-8859-1" )
	datos = f.read()
	lista = string.split(datos, '\n')
	
	contador = 0  
	
	for i in range(desde,len(lista)):
		
		result = re.search(rawstr, lista[i],  re.IGNORECASE| re.MULTILINE)
		if result:
			
			flag = i
				
			month = mesreplace(result.group('month'))
			day = result.group('day')
			if len(day) == 1:
				day = "0"+day
			hour = result.group('hour')
			fecha = time.strftime('%Y') +'-'+ month +'-'+ day +' '+ hour
			
			if result.group('host') != "":
				server = result.group('host')
				
			#server = socket.gethostname()
			#tipo = result.group('type')
			pid = result.group('pid')
			if pid == "":
			    pid = "0"

			usuario = result.group('usuario')
			usuario = string.replace(usuario,'illegal','')
			usuario = string.replace(usuario,'invalid','')
			usuario = string.replace(usuario,'user','')
			usuario = string.replace(usuario,'from','')
			usuario = string.replace(usuario,'=','')
			usuario = string.strip(usuario)
			
			action = result.group('action')

			ip = result.group('ip')
			
			#if tipo == "sshd" or tipo == "proftpd" or tipo == "apache":
			#    ip = result.group('ip')
			#elif tipo == "gdm" or tipo == "su" or tipo == "login":
			#    ip = "127.0.0.1"
			
			detalle = lista[i]
				
			# execute SQL
			try:
				if ip != "":
				    sql = "INSERT INTO login (fecha,server,tipo,pid,action,usuario,ip,detalle) VALUES('"+fecha+"','"+server+"','"+tipo+"','"+pid+"','"+action+"','"+usuario+"','"+ip+"','"+detalle+"')"
				else:
				    sql = "INSERT INTO login (fecha,server,tipo,pid,action,usuario,detalle) VALUES('"+fecha+"','"+server+"','"+tipo+"','"+pid+"','"+action+"','"+usuario+"','"+detalle+"')"
				
				inserta.execute(sql)
				#print sql
				contador = contador + 1
				obj_flag.flaginsertar(flag)
				
			except NameError, x:
				#llama al objeto Send_Error()
				obj_send_email.excepcion(lista[i],x,str(tipo))
	
	
	# SHOW SCREEN DETAIL
	print "["+section+"]"
	print "path: "+archivolog
	print "inserts: "+str(contador)
	print "-------------------------------"
	
	print ""
	

def regexFilters():
	rawstr = r"""(?P<section>(?<=^\[)\w+)"""
	compile_obj = re.compile(rawstr,  re.IGNORECASE| re.MULTILINE)

	file = open(CONFIG_PATH+"filters.conf")
	datos = file.read()

	for result in compile_obj.finditer(datos):
		
		section = config_filters[result.group('section')]
		CONFIG_ENABLED = section['enabled']
		CONFIG_TYPE = section['type']
		CONFIG_LOG = section['log']
		CONFIG_ACTION = section['action']
		CONFIG_REGEX = section['regex']
		
		# Execute regex parser
		if CONFIG_ENABLED == "true":
			existe = os.path.exists(CONFIG_LOG) 
			if existe:
				regexAction(CONFIG_TYPE,CONFIG_LOG,CONFIG_REGEX,result.group('section'),CONFIG_ACTION)
				
				# UPDATE IP SSHD session 
				if result.group('section') == "sshd_session" and string.lower(UPDATE_IP_SSHD) == "y":
					updateipsshd()
				
				# UPDATE IP SMBD session (DISABLED)
				##if result.group('section') == "smbd_session":
				##	updateipsmbd()
								
				
			else:
				print "["+str(result.group('section'))+"]"
				print "Log File "+CONFIG_LOG+" not exist."
				print "-------------------------------"
				print ""
		
		#print section


def send_email(subject,email,msg_body):
	
	import smtplib
	from email.MIMEText import MIMEText
	
	# connect
	db = MySQLdb.connect(host=CONFIG_HOST, user=CONFIG_USER, passwd=CONFIG_PASS, db=CONFIG_DB)
	# create a cursor
	cursor = db.cursor(MySQLdb.cursors.DictCursor)
	# execute SQL
	sql = "SELECT * from smtp_config "
	cursor.execute(sql)
	result = cursor.fetchall()
	
	for record in result:
		smtp_server = record["smtp_server"]
		port_server = record["smtp_port"]
		mail_from = record["mail_from"]
		auth_active = record["auth_active"]
		auth_user = record["auth_user"]
		auth_pass = record["auth_pass"]
	
	msg = MIMEText(msg_body)

	msg['Subject'] = str(subject)
	msg['From'] = mail_from
	msg['Reply-to'] = mail_from
	msg['To'] = str(email)

	s = smtplib.SMTP()
	s.connect(smtp_server)
	
	# if required authenticate
	if auth_active == 1:
		s.login(auth_user,auth_pass)
	
	if email == "":
		email = mail_from
		
	# Send the email - real from, real to, extra headers and content ...
	s.sendmail(mail_from, str(email), msg.as_string())
	s.close()


class Send_Error:
	"agrega registros en el archivo log de errores y controla que no se repitan. "
	def __init__(self): 
		self.send_error = ""
		
	def excepcion(self,lista,x,tipo_error):
		error_flag = 0
		
		f_error = open( CONFIG_PATH + "error.d/error."+str(today)+".log" )
		datos_error = f_error.read()
		lista_error = string.split(datos_error, '\n')

		for i_error in range(0,len(lista_error)):
			if lista.replace('\n','') == lista_error[i_error]:
				error_flag = 1
		
		if error_flag == 0:
			print '[error]: ', x
			os.system("echo '["+tipo_error+" error]: " + str(x) + "' >> "+CONFIG_PATH+"error.d/error."+str(today)+".log")
			os.system("echo '" + lista + "' >> "+CONFIG_PATH+"error.d/error."+str(today)+".log")
			self.send_error = 1

	def send(self):
		if self.send_error == 1:
			#os.system("mail "+CONFIG_EMAIL_TO+" -s '[auth2db error]' < "+CONFIG_PATH+"error.d/error."+str(today)+".log")
			
			msg = "Please send the /etc/auth2db/error.d/error."+str(today)+".log to the Developers.\n"
			msg = msg + "To check and FIX Auth2DB.\n"
			msg = msg + "Thanks. \n\n"
			
			email = ""
			send_email("[auth2db error] ", email, msg)
			
			print "Please send the /etc/auth2db/error.d/error."+str(today)+".log to the Developers."
			print "To check and FIX Auth2DB."
			print "Thanks. \n\n"


class Flag:
	"Controla las banderas del ultimo insert"
	def __init__(self): 
		self.existe = ""
		self.f = ""
		self.flag = ""
		self.datos = ""
		self.lista = ""
		self.str = ""
		self.line_flag = ""
		self.i = ""     
		self.str = ""
		self.tipo = ""
		self.section = ""
		
	def flaginiciar(self):
		self.existe = os.path.exists(CONFIG_PATH_FLAG+"flag."+self.section+self.str+".dat")  

		if self.existe:
			self.f = open(CONFIG_PATH_FLAG+"flag."+self.section+self.str+".dat",'r')
			self.flag = self.f.readline()
			if self.flag == "":
				os.system("echo 'ok' > "+CONFIG_PATH_FLAG+"flag."+self.section+self.str+".dat")
		else:
			os.system("echo 'ok' > "+CONFIG_PATH_FLAG+"flag."+self.section+self.str+".dat")           

	def flaginsertar(self, linea):
		self.f = open( CONFIG_PATH_TMP+"auth2db_"+self.section+self.str+'.log' )
		self.datos = self.f.read()
		self.lista = string.split(self.datos, '\n')
		
		self.bandera = "echo '" + self.lista[linea] + "' > "+CONFIG_PATH_FLAG+"flag."+self.section+self.str+".dat"
		os.system(self.bandera) 

	def flagcheck(self):
		self.f = open(CONFIG_PATH_FLAG+"flag."+self.section+self.str+".dat",'r')
		self.line_flag = self.f.readline()

		self.f = open( CONFIG_PATH_TMP+"auth2db_"+self.section+self.str+'.log' )
		self.datos = self.f.read()
		self.lista = string.split(self.datos, '\n')

		self.flag = ''
		for self.i in range(0,len(self.lista)):
		#os.system("echo '"+line_flag+str(len(line_flag))+" - "+lista[i]+str(len(lista[i]))+"' >> test.txt")
			if self.line_flag.replace('\n','') == self.lista[self.i]:
				self.flag = self.i

		return self.flag


def updateipsmbd():
	# connect
	db = MySQLdb.connect(host=CONFIG_HOST, user=CONFIG_USER, passwd=CONFIG_PASS, db=CONFIG_DB)

	# create a cursor
	cursor = db.cursor()
	# execute SQL
	#sql = "SELECT distinct pid,usuario from login WHERE (ip is NULL OR ip = '' OR ip = '-') AND tipo = 'smbd'";
	sql = "SELECT distinct pid,usuario from login WHERE ip is NULL AND tipo = 'smbd'";
	cursor.execute(sql)
	result = cursor.fetchall()
	
	for record in result:
		pid = str(record[0])
		usuario = record[1]
		
		os.system("grep -r "+pid+" /var/log/samba/* > "+CONFIG_PATH_TMP+"tmpfile")
		f = open(CONFIG_PATH_TMP+"tmpfile")
		tmp = f.readline()
			
		if tmp != "":
			
			pos = tmp.index(":")
			linea = tmp[pos+3:len(tmp)]

			linea_array = string.split(linea, ' ')
			machine = linea_array[0]
			
			ip = linea_array[1].replace("(","")
			ip = ip.replace(")","")
			
			actualiza = db.cursor()
			sql = "UPDATE login SET ip='"+ip+"', machine='"+machine+"' WHERE pid='"+pid+"' AND usuario='"+usuario+"' AND ip is NULL"
			actualiza.execute(sql)
		else:
			actualiza = db.cursor()
			sql = "UPDATE login SET ip='0.0.0.0' WHERE pid='"+pid+"' AND usuario='"+usuario+"' AND ip is NULL"
			actualiza.execute(sql)
		
def updateipsshd():
	# connect
	db = MySQLdb.connect(host=CONFIG_HOST, user=CONFIG_USER, passwd=CONFIG_PASS, db=CONFIG_DB)
	
	# create a cursor
	cursor = db.cursor()
	# execute SQL
	sql = "UPDATE login AS t1 LEFT JOIN login AS t2 ON t2.action =  'Accepted' AND t2.tipo = 'sshd' AND t2.fecha = t1.fecha	SET t1.ip = t2.ip WHERE t1.action = 'opened' AND t1.tipo = 'sshd' AND t1.ip is NULL"
	cursor.execute(sql)
	
	sql = "UPDATE login SET ip = '0.0.0.0' WHERE action = 'opened' AND tipo = 'sshd' AND ip is NULL"
	cursor.execute(sql)


# Objeto "Flag()" Global
obj_flag = Flag()
obj_flag_pass = Flag()

# Objeto "Send()" Global
obj_send_email = Send_Error()



def main():
	
	# Evalua si se paso el argumento -v
	if len(sys.argv) > 1 and sys.argv[1] == "-v":
		print "\nAuth2DB"
		print "version: " + __version__
		print "author: " + __author__
		print "email: ezequielvera@yahoo.com.ar"
		print "website: http://www.auth2db.com.ar"
		print "copyright: " + __copyright__
		print ""
		
	else:
		# Execute regexFilters()
		regexFilters()


	obj_send_email.send()



#Esta linea lanza la funcion principal si aun no esta lanzada
if __name__ =='__main__':
	try:
		main()
	except:
		# print error message re exception
		traceback.print_exc()
