''' Report interscanners for sagator (c) 2003-2020 Jan ONDREJ (SAL) 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. ''' from __future__ import absolute_import from avlib import * from .match import match_any __all__=['report', 'report_recipients'] ##################################################################### ### REPORT class # MESSAGE REPORT TEMPLATE REPORT_MSG_TEMPLATE = '''\ From: %(FROM)s To: %(TO)s Subject: ALERT, %(VIRNAME)s FROM %(SENTBY_IP)s! VIRUS FOUND! --------------------------------------------------------------------- STATUS: %(STATUS)s QUARANTINED AS: %(QNAME)s --------------------------------------------------------------------- SMTP COMMUNICATION: %(SMTP_COMM)s --------------------------------------------------------------------- SCANNER: %(SCANNER_NAME)s %(SCANNER_OUTPUT)s --------------------------------------------------------------------- HEADER OF MESSAGE: %(MSG_HEADER)s ''' class report(match_any): ''' Report any message to admin, user or anybody. This module can be used to report an message to administrator or to any user. You can use following variables in report messages: %(FROM)s report sender %(VIRNAME)s detected virus name %(SMTP_COMM)s SMTP communication %(STATUS)s status of this message (REJECTED, DROPPED, ...) %(QNAME)s filename in quarantine %(QQNAME)s quoted (urlencoded) filename in quarantine %(SCANNER_OUTPUT)s returned scanner output %(SCANNER_NAME)s name of the scanner %(MSG_HEADER)s virus/spam header %(MSG_BODY)s virus/spam body (without header) %(SUBJECT)s message's header Subject %(RANDOM)s 10 random characters (useable to generate a boundary) %(SENDER)s virus/spam sender %(RECIPIENTS)s virus/spam recipients joined by comma (',') %(TO)s report recipeints %(VERSION)s sagator's version %(SENTBY_IP)s sender's IP %(SENTBY_NAME)s sender's hostname %(SENTBY_HELO)s sender's HELO/EHLO string Usage: report(recipients, msg, scanners) or: report(...).sender('') or: report(...).ifscan(cond_scanner) Where: recipients is an array of recipients, like: ['', '', ...] You can use variables mentioned above in this array, but only %(SENDER)s can be used logically. msg is an message template, you can use report.MSG_TMPL cond_scanner is a scanner, which defines, when report may be sent Examples: - send report to admin@localhost: report([''], report.MSG_TMPL, ... ) - send report to email sender: report(['%(SENDER)s'], report.MSG_TMPL, ... ) ''' # Templates MSG_TMPL = REPORT_MSG_TEMPLATE # scanner name = 'report()' def __init__(self, recipients, msg, *scanners): self.MSG = msg self.RECIPIENTS = recipients self.SENDER = '' match_any.__init__(self, scanners) def sender(self, email): self.SENDER = email return self def ifscan(self, scanner): self.IFSCAN = scanner return self def ifscan_main(self, buffer, args={}): try: level, detected, virlist = self.IFSCAN.scanbuffer(buffer, args) except (NameError, AttributeError): return True if is_infected(level, detected): debug.echo(6, 'report(): condition: True (%f, %s)' % (level, tostr(detected))) return True else: debug.echo(6, 'report(): condition: False (%f, %s)' % (level, tostr(detected))) return False def scanbuffer(self, buffer, args={}): level, detected, virlist = match_any.scanbuffer(self, buffer, args) if globals.scan_only: return level, detected, virlist if not self.ifscan_main(buffer, args): return level, detected, virlist if not is_infected(level, detected): return level, detected, virlist if self.RECIPIENTS: sender = mail.getsender() repl_vars = { 'FROM': self.SENDER, 'VIRNAME': tostr(detected), 'SMTP_COMM': tostr(mail.noop_connect_from()+mail.comm.rstrip()), 'STATUS': tostr(globals.action(level)), 'QNAME': globals.QFNAME, 'QQNAME': quote_plus(globals.QFNAME), 'SCANNER_OUTPUT': "".join(tostr_list(virlist)), 'SCANNER_NAME': globals.found_by.name, 'MSG_HEADER': tostr(mail.data[:mail.bodypos].rstrip()), 'MSG_BODY': tostr(mail.data[mail.bodypos:]), 'SUBJECT': mail.headers.get('Subject'), 'RANDOM': randomchars(10), 'SENDER': tostr(mail.sender), 'RECIPIENTS': tostr(','.join(mail.recip)), 'VERSION': SG_VER_REL, 'SENTBY_IP': tostr(sender['ADDR']), 'SENTBY_NAME': tostr(sender['NAME']), 'SENTBY_HELO': tostr(sender['HELO']) } recipients = [replace_tmpl(x, repl_vars) for x in self.RECIPIENTS] repl_vars['TO'] = ',\r\n\t'.join(recipients) msg = replace_tmpl(self.MSG, repl_vars) try: smtpc().sendmail(self.SENDER, recipients, msg) except SmtpcError as err: (ses, es) = err.args debug.echo(1, "report(): AdminReport: ", ses) return level, detected, virlist class report_recipients(report): ''' Report any message to email recipients. This module can be used to report an message to mail recipients. For message description see report() scanner. Usage: report_recipients(msg, scanners) or: report_recipients(...).sender('') or: report(...).ifscan(cond_scanner) Where: msg is an message template, you can use report.MSG_TMPL scanners are scanners which are used cond_scanner is a scanner, which defines, when report may be sent ''' # Templates MSG_TMPL = REPORT_MSG_TEMPLATE # scanner name = 'report_recipients()' def __init__(self, msg, *scanners): self.MSG = msg self.SENDER = '' match_any.__init__(self, scanners) def scanbuffer(self, buffer, args={}): level, detected, virlist = match_any.scanbuffer(self, buffer, args) if globals.scan_only: return level, detected, virlist if not self.ifscan_main(buffer, args): return level, detected, virlist if not is_infected(level, detected): return level, detected, virlist sender = mail.getsender() repl_vars = { 'FROM': self.SENDER, 'VIRNAME': detected, 'SMTP_COMM': mail.noop_connect_from()+mail.comm.rstrip(), 'STATUS': globals.action(level), 'QNAME': globals.QFNAME, 'QQNAME': quote_plus(globals.QFNAME), 'SCANNER_OUTPUT': "".join(virlist), 'SCANNER_NAME': globals.found_by.name, 'MSG_HEADER': mail.data[:mail.bodypos].rstrip(), 'MSG_BODY': mail.data[mail.bodypos:], 'RANDOM': randomchars(10), 'SENDER': mail.sender, 'RECIPIENTS': ','.join(mail.recip), 'TO': ',\r\n\t'.join(mail.recip), 'VERSION': SG_VER_REL, 'SENTBY_IP': sender['ADDR'], 'SENTBY_NAME': sender['NAME'], 'SENTBY_HELO': sender['HELO'] } msg = replace_tmpl(self.MSG, repl_vars) try: smtpc().sendmail(self.SENDER, mail.recip, msg) except SmtpcError as err: (ses, es) = err.args debug.echo(1, "report(): UserReportRecipients: ", ses) return level, detected, virlist