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'] self.smtp_user = mr_ref['smtp_user']
if 'smtp_pass' not in mr_ref: if 'smtp_pass' not in mr_ref:
raise MailrelayException("'smtp_pass' is mandatory!") 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: if 'comment' in mr_ref:
self.comment = mr_ref['comment'] self.comment = mr_ref['comment']
if 'href' in mr_ref: if 'href' in mr_ref:
@ -99,6 +99,9 @@ class Mailbox:
if 'imap_separator' not in mb_ref: if 'imap_separator' not in mb_ref:
raise MailboxException("'imap_separator' is mandatory!") raise MailboxException("'imap_separator' is mandatory!")
self.imap_seperator = mb_ref['imap_separator'] 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: if 'comment' in mb_ref:
self.comment = mb_ref['comment'] self.comment = mb_ref['comment']
if 'href' in mb_ref: if 'href' in mb_ref:

View File

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

View File

@ -179,7 +179,7 @@ class GulagDB:
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(whoami(self) + str(e.msg)) from e raise GulagDBException(whoami(self) + str(e.msg)) from e
def get_mailrelay(self,mailbox_id): def get_mailrelay(self,mailrelay_id):
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
cursor.execute( 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 import sys,re,urllib
from urllib.parse import urlparse from urllib.parse import urlparse
from smtplib import SMTP
def whoami(obj): def whoami(obj):
return type(obj).__name__ + "::" + sys._getframe(1).f_code.co_name + "(): " 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): def extract_uris(input_text):
uris = {} uris = {}
uri_pattern = r'(https?:\/\/[^\s<>"]+)' uri_pattern = r'(https?:\/\/[^\s<>"]+)'

View File

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