Bounce a QuarMail

This commit is contained in:
Dominik Chilla 2019-01-14 02:01:25 +01:00
parent 46a6eaccae
commit 4d93d33e34
6 changed files with 148 additions and 35 deletions

View File

@ -38,7 +38,7 @@ class Mailrelay:
self.smtp_user = mr_ref['smtp_user']
if 'smtp_pass' not in mr_ref:
raise MailrelayException("'smtp_pass' is mandatory!")
self.smtp_pass = mb_ref['imap_pass']
self.smtp_pass = mr_ref['smtp_pass']
if 'comment' in mr_ref:
self.comment = mr_ref['comment']
if 'href' in mr_ref:
@ -99,6 +99,9 @@ class Mailbox:
if 'imap_separator' not in mb_ref:
raise MailboxException("'imap_separator' is mandatory!")
self.imap_seperator = mb_ref['imap_separator']
if 'mailrelay_id' not in mb_ref:
raise MailboxException("'mailrelay_id' is mandatory!")
self.mailrelay_id = mb_ref['mailrelay_id']
if 'comment' in mb_ref:
self.comment = mb_ref['comment']
if 'href' in mb_ref:

View File

@ -4,6 +4,7 @@ from GulagDB import (
GulagDB,GulagDBException,GulagDBNotFoundException,GulagDBBadInputException
)
from GulagMailbox import IMAPmailbox,IMAPmailboxException
from GulagMailrelay import GulagMailrelay,GulagMailrelayException
from GulagUtils import whoami,extract_uris,extract_fqdn
import ssdeep, hashlib
@ -305,16 +306,10 @@ class Gulag:
raise GulagException(whoami(self) + e.message) from e
imap_mb.close()
# end for mailboxes
if 'rfc822_message' in args:
return {
"quarmails": qms_db,
"rfc822_messages": mailboxes
}
elif 'headers' in args:
return {
"quarmails": qms_db,
"headers": mailboxes
}
return {
"quarmails": qms_db,
"rfc822_messages": mailboxes
}
def get_quarmail(self,args):
qm_db = None
@ -344,8 +339,8 @@ class Gulag:
qm_db['imap_uid']
).decode("utf-8")
elif 'headers' in args:
qm_db['headers'] = imap_mb.get_headers(
qmat_db['imap_uid']
qm_db['rfc822_message'] = imap_mb.get_headers(
qm_db['imap_uid']
)
imap_mb.close()
return qm_db
@ -431,16 +426,28 @@ class Gulag:
def bounce_quarmail(self,args):
try:
# get quarmail object with headers from mailbox
quarmail = self.get_quarmail({
"quarmail_id": args['quarmail_id'],
"rfc822_message": True
"headers": True
})
# TODO: bounce quarmail headers-only to quarmail['env_from']
# TODO: self.delete_quarmail() if arg['purge']
# the mailbox reference holds the appropriate mailrelay_id
mailbox_ref = self.db.get_mailbox(quarmail['mailbox_id'])
logging.info(whoami(self)+"mailrelay_id: "+str(mailbox_ref['mailrelay_id']))
mailrelay_ref = self.db.get_mailrelay(mailbox_ref['mailrelay_id'])
logging.info(whoami(self) + str(mailrelay_ref))
mailrelay = GulagMailrelay(mailrelay_ref)
mailrelay.bounce_quarmail(quarmail)
if 'purge' in args:
self.delete_quarmail({"quarmail_id": args['quarmail_id']})
except GulagNotFoundException as e:
raise GulagNotFoundException(whoami(self) + e.message) from e
except GulagException as e:
raise GulagException(whoami(self) + e.message) from e
except GulagDBNotFoundException as e:
raise GulagNotFoundException(whoami(self) + e.message) from e
except GulagMailrelayException as e:
raise GulagException(whoami(self) + e.message) from e
def forward_quarmail(self,args):
try:

View File

@ -179,7 +179,7 @@ class GulagDB:
except mariadb.Error as e:
raise GulagDBException(whoami(self) + str(e.msg)) from e
def get_mailrelay(self,mailbox_id):
def get_mailrelay(self,mailrelay_id):
try:
cursor = self.conn.cursor()
cursor.execute(

116
app/GulagMailrelay.py Normal file
View File

@ -0,0 +1,116 @@
from smtplib import (
SMTP,SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused, SMTPDataError,
SMTPNotSupportedError
)
import email
from email.message import EmailMessage
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.message import MIMEMessage
from email.utils import formatdate
from email import policy
import time,sys
from GulagUtils import whoami
class GulagMailrelayException(Exception):
message = None
def __init__(self,message):
self.message = str(message)
class GulagMailrelay:
id = None
smtp_server = None
smtp_port = None
smtp_security = None
smtp_user = None
smtp_pass = None
mailrelay = None
def __init__(self,mailrelay_ref):
self.id = mailrelay_ref['id']
self.smtp_server = mailrelay_ref['smtp_server']
self.smtp_port = mailrelay_ref['smtp_port']
self.smtp_security = mailrelay_ref['smtp_security']
self.smtp_user = mailrelay_ref['smtp_user']
self.smtp_pass = mailrelay_ref['smtp_pass']
def release_quarmail(self,quarmail):
try:
# FIXME: SMTP transport security and authentication!
with SMTP(host=self.smtp_server,port=self.smtp_port) as self.mailrelay:
self.mailrelay.sendmail(
quarmail['env_from'],
quarmail['env_rcpt'],
quarmail['rfc822_message']
)
self.mailrelay.quit()
except (SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused,
SMTPDataError,SMTPNotSupportedError) as e:
raise GulagMailrelayException(whoami(self) + e.message) from e
except TimeoutError as e:
raise GulagMailrelayException(whoami(self) + e.message) from e
except ConnectionRefusedError as e:
raise GulagMailrelayException(whoami(self) + e.strerror) from e
def bounce_quarmail(self,quarmail):
msg = None
if quarmail['env_from'] == '<>':
raise GulagMailrelayException(whoami(self) +
"Unwilling to double-bounce QuarMail("+quarmail['id']+")!"
)
try:
# multipart/report
msg = MIMEMultipart('report', boundary='GULAG-DSN-BOUNDARY')
msg['Subject'] = 'Undelivered Mail Returned to Sender'
msg['From'] = 'GULAG MAILER-DAEMON (Mail Quarantine System) <>'
msg['To'] = quarmail['env_from']
msg['Auto-Submitted'] = 'auto-replied'
msg['Message-ID'] = '<TODO-something-random@kiss.ass>'
msg['Date'] = formatdate()
msg.preamble = 'This is a MIME-encapsulated message.\r\n'
# text/plain
nt = "This is the mail system at host TODO-GULAG-QUARANTINE.HOST\r\n\r\n"
nt += "I'm sorry to have to inform you that your message could not\r\n"
nt += "be delivered to one or more recipients. It's headers are attached "
nt += "below.\r\n\r\n"
nt += "For further assistance, please send mail to postmaster.\r\n\r\n"
nt += "If you do so, please include this problem report. You can\r\n"
nt += "delete your own text from the attached returned message.\r\n\r\n"
nt += "<"+quarmail['env_rcpt']+">: host GULAG-QUARANTINE.HOST said: 550\r\n"
nt += "Requested action not taken: DANGEROUS\r\n"
msg.attach(MIMEText(nt, 'plain'))
# message/delivery-status
dr = "Reporting-MTA: dns; GULAG-QUARANTINE.HOST\r\n"
dr += "Queue-ID: "+quarmail['mx_queue_id']+"\r\n"
dr += "Sender: rfc822; "+quarmail['env_from']+"\r\n"
dr += "Arrival-Date: "+quarmail['ctime']
dr += "Final-Recipient: rfc822;"+quarmail['env_rcpt']+"\r\n\r\n"
dr += "Original-Recipient: rfc822;"+quarmail['env_rcpt']+"\r\n"
dr += "Action: failed\r\n"
dr += "Status: 5.0.0\r\n"
dr += "Remote-MTA: dns; GULAG-QUARANTINE.HOST\r\n"
dr += "Diagnostic-Code: smtp; 550 Requested action not taken: DANGEROUS\r\n"
dr_part = MIMEBase('message','delivery-status')
dr_part.set_payload(dr)
#dr_part.policy = policy.compat32
#msg.attach(dr_part)
# message/rfc822
msg.attach(MIMEMessage(
email.message_from_bytes(quarmail['rfc822_message'])
))
except:
raise GulagMailrelayException(whoami(self) + str(sys.exc_info()))
try:
# FIXME: SMTP transport security and authentication!
with SMTP(host=self.smtp_server,port=self.smtp_port) as self.mailrelay:
self.mailrelay.sendmail('<>', quarmail['env_from'], msg.as_string())
self.mailrelay.quit()
return True
except (SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused,
SMTPDataError,SMTPNotSupportedError) as e:
raise GulagMailrelayException(whoami(self) + e.message) from e
except TimeoutError as e:
raise GulagMailrelayException(whoami(self) + e.message) from e
except ConnectionRefusedError as e:
raise GulagMailrelayException(whoami(self) + e.strerror) from e

View File

@ -1,26 +1,9 @@
import sys,re,urllib
from urllib.parse import urlparse
from smtplib import SMTP
def whoami(obj):
return type(obj).__name__ + "::" + sys._getframe(1).f_code.co_name + "(): "
def send_mail(args):
try:
# FIXME: SMTP transport security and authentication!
# with SMTP(host=mailbox['smtp_server'],port=mailbox['smtp_port']) as smtp:
# try:
# smtp.sendmail(
# request.headers.get('X-Rspamd-From'),
# mailbox_id,
# msg
# )
# except (SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused,SMTPDataError) as e:
# raise GulagException(str(e)) from e
print("TODO")
except TimeoutError as e:
raise Exception('xyz') from e
def extract_uris(input_text):
uris = {}
uri_pattern = r'(https?:\/\/[^\s<>"]+)'

View File

@ -8,7 +8,7 @@ from Resources import (ResRoot,ResMailboxes,
ResQuarMails,ResQuarMail,ResQuarMailAttachments,
ResQuarMailAttachment,ResAttachments,ResAttachment,
ResRspamd2Mailbox,ResQuarMailURIs,ResQuarMailURI,
ResMailradar2Mailbox,ResQuarMailRelease
ResMailradar2Mailbox,ResQuarMailRelease,ResQuarMailBounce
)
parser = argparse.ArgumentParser()
parser.add_argument('--config', required=True, help="Path to config file")
@ -44,6 +44,10 @@ try:
'/api/v1/quarmails/<int:quarmail_id>/release',
resource_class_kwargs={'gulag_object': gulag}
)
api.add_resource(ResQuarMailBounce,
'/api/v1/quarmails/<int:quarmail_id>/bounce',
resource_class_kwargs={'gulag_object': gulag}
)
api.add_resource(ResQuarMailAttachments,
'/api/v1/quarmails/<int:quarmail_id>/attachments',
resource_class_kwargs={'gulag_object': gulag}