Пример #1
0
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    num_recipients = 0

    # count number of email addresses in To and Cc headers
    for header_keyword in ["To", "Cc"]:
        if header_keyword in email:
            for header in email.get_all(header_keyword):
                email_addresses = extract_email_addresses(str(header))

                if email_addresses:
                    num_recipients += len(email_addresses)

    if num_recipients > config.recipient_limit:
        return ReturnCode.LIMIT_EXCEEDED

    return ReturnCode.LIMIT_OK
Пример #2
0
def main(args):
    try:
        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    for part in email.walk():
        if part.get_content_type() == "text/html":
            charset_mime = part.get_content_charset()

            if charset_mime:
                content = part.get_payload(decode=True).decode(charset_mime,
                                                               errors="ignore")

                match = re.search(r"<meta [^>]*charset=\"?([^;\"> ]+)",
                                  content,
                                  flags=re.IGNORECASE)

                if match is not None:
                    charset_meta = match.group(1).lower()

                    if charset_mime != charset_meta:
                        content = re.sub(
                            r"(<meta [^>]*charset=\"?)({})".format(
                                charset_meta),
                            r"\1{}".format(charset_mime),
                            content,
                            flags=re.IGNORECASE)

                        if HEADER_CTE in part:
                            del part[HEADER_CTE]

                        part.set_payload(content, charset=charset_mime)

                        try:
                            with open(args.input, "wb") as f:
                                f.write(email.as_bytes())
                        except Exception:
                            write_log(args.log,
                                      "Error writing '{}'".format(args.input))

                            return ReturnCode.ERROR

                        return ReturnCode.CHARSET_CHANGED

    return ReturnCode.NOT_MODIFIED
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    email = email.as_bytes()

    for list_string in config.search_strings:
        for string in list_string:
            if email.find(string.encode(CHARSET_UTF8)) == -1:
                break
        else:
            return ReturnCode.STRING_FOUND

    return ReturnCode.NOT_FOUND
Пример #4
0
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if "Sensitivity" in email and str(email.get("Sensitivity")) == "private":
        if len(email.as_bytes()) > config.max_size_kb * 1024:
            write_log(args.log, "Mail exceeds max size")

            return ReturnCode.INVALID

        for part in email.walk():
            if part.is_attachment():
                write_log(args.log, "Mail has attachment")

                return ReturnCode.INVALID

        return ReturnCode.PRIVATE

    return ReturnCode.NOT_PRIVATE
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        check_function = get_expression_list(config.name_expression_list).pop()
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        exec(check_function, globals())

        return check_email(email, args.log)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if "Subject" not in email:
        return ReturnCode.ENCRYPTION_SKIPPED

    header_subject = str(email.get("Subject"))

    keyword_escaped = re.escape(config.keyword_encryption)

    if not re.match(r"^{}".format(keyword_escaped), header_subject,
                    re.IGNORECASE):
        return ReturnCode.ENCRYPTION_SKIPPED

    if "From" not in email:
        write_log(args.log, "Header from does not exist")

        return ReturnCode.ERROR

    header_from = str(email.get("From"))

    if not header_from:
        write_log(args.log, "Header from is empty")

        return ReturnCode.ERROR

    address_sender = extract_email_address(header_from)

    if not address_sender:
        write_log(args.log, "Cannot find sender address")

        return ReturnCode.ERROR

    address_recipient = dict()

    # collect email addresses in To and Cc headers
    for header_keyword in ["To", "Cc"]:
        address_recipient[header_keyword] = set()

        if header_keyword in email:
            for header in email.get_all(header_keyword):
                email_addresses = extract_email_addresses(str(header))

                if email_addresses:
                    address_recipient[header_keyword] |= email_addresses

    if not address_recipient["To"]:
        write_log(args.log, "Cannot find recipient address")

        return ReturnCode.ERROR

    # remove encryption keyword from subject header
    email = re.sub(r"(\n|^)Subject: *{} *".format(keyword_escaped),
                   r"\1Subject: ",
                   email.as_string(),
                   count=1,
                   flags=re.IGNORECASE)
    header_subject = re.sub(r"^{} *".format(keyword_escaped),
                            "",
                            header_subject,
                            flags=re.IGNORECASE)

    password_characters = string.ascii_letters + string.digits
    if config.password_punctuation:
        password_characters += string.punctuation
    password = "".join(
        random.choice(password_characters)
        for i in range(config.password_length))

    try:
        zip_archive = zip_encrypt({("email.eml", email)}, password)
    except Exception:
        write_log(args.log, "Error zip-encrypting email")

        return ReturnCode.ERROR

    # send email with encrypted original mail attached to recipients
    email_message = EmailMessage()
    email_message["Subject"] = header_subject
    email_message["From"] = address_sender
    email_message["To"] = ", ".join(address_recipient["To"])
    if address_recipient["Cc"]:
        email_message["Cc"] = ", ".join(address_recipient["Cc"])
    email_message.set_content(MESSAGE_RECIPIENT.format(address_sender))
    email_message.add_attachment(zip_archive,
                                 maintype="application",
                                 subtype="zip",
                                 filename="email.zip")

    try:
        with smtplib.SMTP("localhost", port=PORT_SMTP) as s:
            s.send_message(email_message)
    except Exception:
        write_log(args.log, "Cannot send recipient email")

        return ReturnCode.ERROR

    # send email with password to sender
    email_message = EmailMessage()
    email_message["Subject"] = "Re: {}".format(header_subject)
    email_message["From"] = address_sender
    email_message["To"] = address_sender
    email_message.set_content(MESSAGE_SENDER.format(password))

    try:
        with smtplib.SMTP("localhost", port=PORT_SMTP) as s:
            s.send_message(email_message)
    except Exception:
        write_log(args.log, "Cannot send sender email")

    return ReturnCode.MAIL_ENCRYPTED
def main(args):
    HEADER_CTE = "Content-Transfer-Encoding"

    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    sys.setrecursionlimit(RECURSION_LIMIT)

    if config.text_tag or (args.remove and config.address_tag
                           and config.clean_text):
        text_part = None

        for part in email.walk():
            if part.get_content_type(
            ) == "text/plain" and not part.is_attachment():
                text_part = part
                text_charset = python_charset(text_part.get_content_charset())

                if text_charset is None:
                    text_charset = CHARSET_UTF8

                try:
                    text_content = text_part.get_payload(decode=True).decode(
                        text_charset, errors="ignore").replace("\r", "")
                except Exception:
                    write_log(
                        args.log,
                        "Cannot decode text part with charset '{}'".format(
                            text_charset))

                    return ReturnCode.INVALID_ENCODING

                break

    if config.html_tag or (args.remove and config.address_tag
                           and config.clean_html):
        html_part = None

        for part in email.walk():
            if part.get_content_type(
            ) == "text/html" and not part.is_attachment():
                html_part = part
                html_charset = python_charset(html_part.get_content_charset())

                if html_charset is None:
                    html_charset = CHARSET_UTF8

                try:
                    html_content = html_part.get_payload(decode=True).decode(
                        html_charset, errors="ignore")
                except Exception:
                    write_log(
                        args.log,
                        "Cannot decode html part with charset '{}'".format(
                            html_charset))

                    return ReturnCode.INVALID_ENCODING

                break

    if config.calendar_tag:
        calendar_part = None

        for part in email.walk():
            if part.get_content_type(
            ) == "text/calendar" and not part.is_attachment():
                calendar_part = part
                calendar_charset = python_charset(
                    calendar_part.get_content_charset())

                if calendar_charset is None:
                    calendar_charset = CHARSET_UTF8

                try:
                    calendar_content = calendar_part.get_payload(
                        decode=True).decode(calendar_charset, errors="ignore")
                except Exception:
                    write_log(
                        args.log,
                        "Cannot decode calendar part with charset '{}'".format(
                            calendar_charset))

                    return ReturnCode.INVALID_ENCODING

                break

    email_modified = False

    if args.remove:
        if config.address_tag:
            # remove address tag

            string_tag = "{} ".format(config.address_tag)

            pattern_tag = re.compile(r'^"?{} '.format(
                re.escape(config.address_tag)))

            pattern_quote = re.compile(r'^".*"$')

            for header_keyword in ["To", "Cc"]:
                if header_keyword in email:
                    header = "".join([
                        part if isinstance(part, str) else
                        part.decode(python_charset(encoding), errors="ignore")
                        if encoding else part.decode(CHARSET_UTF8,
                                                     errors="ignore")
                        for (part, encoding) in decode_header(", ".join([
                            str(header)
                            for header in email.get_all(header_keyword)
                        ]).replace("\n", ""))
                    ])

                    list_address = extract_addresses(header)

                    if list_address:
                        header = ""
                        header_modified = False

                        for (prefix, address, suffix) in list_address:
                            if prefix.startswith(";") or prefix.startswith(
                                    ","):
                                prefix = prefix[1:]

                            if prefix.endswith("<") and suffix.startswith(">"):
                                prefix = prefix[:-1]
                                suffix = suffix[1:]

                            prefix = prefix.strip()
                            suffix = suffix.strip()

                            if re.search(pattern_tag, prefix):
                                if prefix.startswith('"'):
                                    prefix = '"' + prefix[
                                        len(config.address_tag) + 2:]
                                else:
                                    prefix = prefix[len(config.address_tag) +
                                                    1:]

                                header_modified = True

                            if not string_ascii(prefix):
                                if re.search(pattern_quote, prefix):
                                    prefix = prefix[1:-1]

                                prefix = make_header([
                                    (prefix.encode(CHARSET_UTF8), CHARSET_UTF8)
                                ]).encode()

                            if prefix:
                                header += prefix + " "

                            header += "<" + address + ">"

                            if suffix:
                                header += " " + suffix

                            header += ", "

                        if header_modified:
                            del email[header_keyword]
                            email[header_keyword] = header[:-2]

                            email_modified = True

        if config.subject_tag and "Subject" in email:
            # remove subject tag

            header = "".join([
                part if isinstance(part, str) else
                part.decode(python_charset(encoding), errors="ignore")
                if encoding else part.decode(CHARSET_UTF8, errors="ignore")
                for (part, encoding) in decode_header(
                    str(email.get("Subject")).strip().replace("\n", ""))
            ])

            match = re.search(r"{} ".format(re.escape(config.subject_tag)),
                              header)

            if match is not None:
                del email["Subject"]
                email["Subject"] = header[:match.start()] + header[match.end(
                ):]

                email_modified = True

        if (config.text_tag or
            (config.address_tag
             and config.clean_text)) and text_part is not None:
            # remove text body tag

            body_modified = False

            if config.text_tag:
                split_tag = config.text_tag.split("\n")

                while not split_tag[-1]:
                    del split_tag[-1]

                pattern_tag = re.compile("\\n".join(
                    [r"(>+ )*" + re.escape(item)
                     for item in split_tag]) + r"\n")

                match = re.search(pattern_tag, text_content)

                if match is not None:
                    text_content = re.sub(pattern_tag, "", text_content)

                    body_modified = True

            if config.address_tag and config.clean_text and string_tag in text_content:
                text_content = text_content.replace(string_tag, "")

                body_modified = True

            if body_modified:
                if HEADER_CTE in text_part:
                    del text_part[HEADER_CTE]

                text_part.set_payload(text_content, charset=text_charset)

                email_modified = True

        if (config.html_tag or
            (config.address_tag
             and config.clean_html)) and html_part is not None:
            # remove html body tag

            body_modified = False

            soup = bs4.BeautifulSoup(html_content, features="html5lib")

            list_tag = soup.find_all("div",
                                     id=re.compile(r".*{}.*".format(
                                         re.escape(config.html_tag_id))))

            if list_tag:
                for tag in list_tag:
                    tag.decompose()

                try:
                    html_content = soup.encode(html_charset).decode(
                        html_charset)
                except Exception:
                    write_log(args.log, "Error converting soup to string")

                    return ReturnCode.ERROR

                body_modified = True

            if config.address_tag and config.clean_html and string_tag in html_content:
                html_content = html_content.replace(string_tag, "")

                body_modified = True

            if body_modified:
                if HEADER_CTE in html_part:
                    del html_part[HEADER_CTE]

                html_part.set_payload(html_content, charset=html_charset)

                email_modified = True

        if config.calendar_tag and calendar_part is not None:
            # remove calendar tag

            match = re.search(r"\nORGANIZER;.*CN=([^:;\r\n]+)",
                              calendar_content)

            if match is not None:
                organizer = match.group(1)
                organizer_start = match.start(1)
                organizer_end = match.end(1)

                match = re.search(
                    r"^{} ".format(re.escape(config.calendar_tag)), organizer)

                if match is not None:
                    if HEADER_CTE in calendar_part:
                        del calendar_part[HEADER_CTE]

                    if calendar_charset != CHARSET_UTF8 and not string_ascii(
                            config.calendar_tag):
                        calendar_charset = CHARSET_UTF8

                    calendar_part.set_payload(
                        calendar_content[:organizer_start] +
                        organizer[match.end():] +
                        calendar_content[organizer_end:],
                        charset=calendar_charset)

                    email_modified = True
    else:
        if config.address_tag and "From" in email:
            # add address tag

            pattern_tag = re.compile(r'^"?{} '.format(
                re.escape(config.address_tag)))

            pattern_quote = re.compile(r'^".*"$')

            list_address = extract_addresses("".join([
                part if isinstance(part, str) else
                part.decode(python_charset(encoding), errors="ignore")
                if encoding else part.decode(CHARSET_UTF8, errors="ignore")
                for (part, encoding) in decode_header(
                    str(email.get("From")).replace("\n", ""))
            ]))

            if list_address:
                (prefix, address, suffix) = list_address[0]

                if prefix.endswith("<") and suffix.startswith(">"):
                    prefix = prefix[:-1]
                    suffix = suffix[1:]

                prefix = prefix.strip()
                suffix = suffix.strip()

                if not re.search(pattern_tag, prefix):
                    prefix_new = ""

                    for (index, char) in enumerate(prefix):
                        if char == '"':
                            if end_escape(prefix[:index]):
                                prefix_new += char
                        else:
                            prefix_new += char

                    if prefix_new:
                        prefix = '"{} {}"'.format(config.address_tag,
                                                  prefix_new)
                    else:
                        prefix = '"{} {}"'.format(config.address_tag, address)

                    if not string_ascii(prefix):
                        if re.search(pattern_quote, prefix):
                            prefix = prefix[1:-1]

                        prefix = make_header([(prefix.encode(CHARSET_UTF8),
                                               CHARSET_UTF8)]).encode()

                    del email["From"]
                    email["From"] = prefix + " <" + address + "> " + suffix

                    email_modified = True

            if config.name_domain_list:
                # add address tag to external addresses in To/Cc header

                try:
                    set_address = get_address_list(config.name_domain_list)
                except Exception as ex:
                    write_log(args.log, ex)

                    return ReturnCode.ERROR

                pattern_domain = re.compile(r"^\S+@(\S+)")

                set_domain = {
                    match.group(1).lower()
                    for match in [
                        re.search(pattern_domain, address)
                        for address in set_address
                    ] if match is not None
                }

                for header_keyword in ["To", "Cc"]:
                    if header_keyword in email:
                        header = "".join([
                            part if isinstance(part, str) else
                            part.decode(python_charset(encoding),
                                        errors="ignore") if encoding else
                            part.decode(CHARSET_UTF8, errors="ignore")
                            for (part, encoding) in decode_header(", ".join([
                                str(header)
                                for header in email.get_all(header_keyword)
                            ]).replace("\n", ""))
                        ])

                        list_address = extract_addresses(header)

                        if list_address:
                            header = ""
                            header_modified = False

                            for (prefix, address, suffix) in list_address:
                                if prefix.startswith(";") or prefix.startswith(
                                        ","):
                                    prefix = prefix[1:]

                                if prefix.endswith("<") and suffix.startswith(
                                        ">"):
                                    prefix = prefix[:-1]
                                    suffix = suffix[1:]

                                prefix = prefix.strip()
                                suffix = suffix.strip()

                                match = re.search(pattern_domain, address)

                                if match and match.group(1).lower(
                                ) not in set_domain and not re.search(
                                        pattern_tag, prefix):
                                    prefix_new = ""

                                    for (index, char) in enumerate(prefix):
                                        if char == '"':
                                            if end_escape(prefix[:index]):
                                                prefix_new += char
                                        else:
                                            prefix_new += char

                                    if prefix_new:
                                        prefix = '"{} {}"'.format(
                                            config.address_tag, prefix_new)
                                    else:
                                        prefix = '"{} {}"'.format(
                                            config.address_tag, address)

                                    header_modified = True

                                if not string_ascii(prefix):
                                    if re.search(pattern_quote, prefix):
                                        prefix = prefix[1:-1]

                                    prefix = make_header([
                                        (prefix.encode(CHARSET_UTF8),
                                         CHARSET_UTF8)
                                    ]).encode()

                                if prefix:
                                    header += prefix + " "

                                header += "<" + address + ">"

                                if suffix:
                                    header += " " + suffix

                                header += ", "

                            if header_modified:
                                del email[header_keyword]
                                email[header_keyword] = header[:-2]

                                email_modified = True

        if config.subject_tag and "Subject" in email:
            # add subject tag

            header = "".join([
                part if isinstance(part, str) else
                part.decode(python_charset(encoding), errors="ignore")
                if encoding else part.decode(CHARSET_UTF8, errors="ignore")
                for (part, encoding) in decode_header(
                    str(email.get("Subject")).strip().replace("\n", ""))
            ])

            if not re.search(r"^{} ".format(re.escape(config.subject_tag)),
                             header):
                header = "{} {}".format(config.subject_tag, header)

                del email["Subject"]
                email["Subject"] = header

                email_modified = True

        if config.text_tag and text_part is not None:
            # add text body tag

            if config.text_top:
                text_content = config.text_tag + text_content
            else:
                text_content += config.text_tag

            if text_charset != CHARSET_UTF8 and not string_ascii(
                    config.text_tag):
                text_charset = CHARSET_UTF8

            if HEADER_CTE in text_part:
                del text_part[HEADER_CTE]

            text_part.set_payload(text_content, charset=text_charset)

            email_modified = True

        if config.html_tag and html_part is not None:
            # add html body tag

            if config.html_top:
                match = re.search(r"<body[^>]*>", html_content)

                if match is not None:
                    index = match.end()
                else:
                    match = re.search(r"<html[^>]*>", html_content)

                    if match is not None:
                        index = match.end()
                    else:
                        index = 0
            else:
                index = html_content.find("</body>")

                if index < 0:
                    index = html_content.find("</html>")

                    if index < 0:
                        index = len(html_content) - 1

            if HEADER_CTE in html_part:
                del html_part[HEADER_CTE]

            if html_charset != CHARSET_UTF8 and not string_ascii(
                    config.html_tag):
                html_charset = CHARSET_UTF8

            html_part.set_payload(html_content[:index] +
                                  '<div id="{}">{}</div>'.format(
                                      config.html_tag_id, config.html_tag) +
                                  html_content[index:],
                                  charset=html_charset)

            email_modified = True

        if config.calendar_tag and calendar_part is not None:
            # add calendar tag

            match = re.search(r"\nORGANIZER;.*CN=([^:;\r\n]+)",
                              calendar_content)

            if match is not None:
                organizer = match.group(1)

                if not re.search(
                        r"^{} ".format(re.escape(config.calendar_tag)),
                        organizer):
                    if HEADER_CTE in calendar_part:
                        del calendar_part[HEADER_CTE]

                    if calendar_charset != CHARSET_UTF8 and not string_ascii(
                            config.calendar_tag):
                        calendar_charset = CHARSET_UTF8

                    calendar_part.set_payload(
                        calendar_content[:match.start(1)] +
                        config.calendar_tag + " " + organizer +
                        calendar_content[match.end(1):],
                        charset=calendar_charset)

                    email_modified = True

    if email_modified:
        try:
            with open(args.input, "wb") as f:
                f.write(email.as_bytes())
        except Exception:
            write_log(args.log, "Error writing '{}'".format(args.input))

            return ReturnCode.ERROR

        return ReturnCode.TAG_ADDED

    return ReturnCode.NOT_MODIFIED
Пример #8
0
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        set_password = get_expression_list(config.name_expression_list)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if not set_password:
        write_log(args.log, "Password list is empty")

        return ReturnCode.ERROR

    try:
        pdf_file = fitz.open(args.input)
    except Exception:
        write_log(args.log, "Cannot open PDF file '{}'".format(args.input))

        return ReturnCode.ERROR

    for password in set_password:
        if pdf_file.authenticate(password) != 0:
            break
    else:
        write_log(args.log, "Decryption failed")

        return ReturnCode.PROBLEM

    list_scan = list()

    if config.scan_sophos:
        list_scan.append(scan_sophos)

    if config.scan_kaspersky:
        list_scan.append(scan_kaspersky)

    if config.scan_avira:
        list_scan.append(scan_avira)

    if list_scan or config.remove_encryption:
        virus_found = None

        with NamedTemporaryFile(dir="/tmp") as path_tmpfile:
            path_file = path_tmpfile.name

            pdf_file.save(path_file)

            pdf_file.close()

            for scan in list_scan:
                try:
                    virus_found = scan(path_file)
                except Exception as ex:
                    write_log(args.log, ex)

                    return ReturnCode.ERROR

                if virus_found is not None:
                    break

            if virus_found is not None:
                write_log(args.log, "Virus '{}'".format(virus_found))

                return ReturnCode.PROBLEM

            if config.remove_encryption:
                copyfile(path_file, args.input)

                return ReturnCode.DECRYPTED

    return ReturnCode.SUCCESS
Пример #9
0
def main(args):
    PATTERN_URL = re.compile(r"(https?://|www\.|ftp\.)\S+", re.IGNORECASE)
    PATTERN_DOMAIN = re.compile(r"^(https?://)?([^/]+)\S*$", re.IGNORECASE)

    try:
        config = get_config(args.config, CONFIG_PARAMETERS)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        image = Image.open(args.input)
    except Exception:
        write_log(args.log, "Cannot read image '{}'".format(args.input))

        return ReturnCode.ERROR

    list_qr = decode(image)

    for qr_code in list_qr:
        try:
            text = qr_code.data.decode(CHARSET_UTF8)
        except Exception:
            text = None

        if text is not None and re.search(PATTERN_URL, text) is not None:
            try:
                set_url = get_url_list(config.name_url_blacklist)
            except Exception as ex:
                write_log(args.log, ex)

                return ReturnCode.ERROR

            set_blacklist = {
                re.compile(url2regex(url), re.IGNORECASE)
                for url in set_url
            }

            try:
                set_url = get_url_list(config.name_url_whitelist)
            except Exception as ex:
                write_log(args.log, ex)

                return ReturnCode.ERROR

            set_whitelist = {
                re.compile(url2regex(url), re.IGNORECASE)
                for url in set_url
            }

            for match in re.finditer(PATTERN_URL, text):
                url = match.group()

                for pattern in set_whitelist:
                    if re.search(pattern, url) is not None:
                        break
                else:
                    for pattern in set_blacklist:
                        if re.search(pattern, url) is not None:
                            write_log(
                                args.log, "'{}' listed on '{}'".format(
                                    url, config.name_url_blacklist))

                            return ReturnCode.MALICIOUS

                    domain = re.search(PATTERN_DOMAIN, url).group(2).lower()

                    while True:
                        blacklist = domain_blacklisted(domain)

                        if blacklist is not None:
                            write_log(
                                args.log, "'{}' listed on '{}'".format(
                                    domain, blacklist))

                            return ReturnCode.MALICIOUS

                        index = domain.find(".")

                        if index < 0:
                            break

                        domain = domain[index + 1:]

    return ReturnCode.OK
Пример #10
0
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        set_password = get_expression_list(config.name_expression_list)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if not set_password:
        write_log(args.log, "Password list is empty")

        return ReturnCode.ERROR

    list_scan = list()

    if config.scan_sophos:
        list_scan.append(scan_sophos)

    if config.scan_kaspersky:
        list_scan.append(scan_kaspersky)

    if config.scan_avira:
        list_scan.append(scan_avira)

    virus_found = None

    if config.remove_encryption:
        buffer = BytesIO()

        zf_decrypted = pyzipper.AESZipFile(buffer, "w", compression=pyzipper.ZIP_LZMA)

    with TemporaryDirectory(dir="/tmp") as path_tmpdir:
        path_tmpdir = Path(path_tmpdir)

        if config.scan_avira:
            path_tmpdir.chmod(0o755)

        with pyzipper.AESZipFile(args.input, "r", compression=pyzipper.ZIP_LZMA, encryption=pyzipper.WZ_AES) as zf:
            for password in set_password:
                try:
                    zf.pwd = password.encode(CHARSET_UTF8)

                    for file_name in zf.namelist():
                        path_file = path_tmpdir.joinpath(file_name)

                        data = zf.read(file_name)

                        try:
                            with open(path_file, "wb") as f:
                                f.write(data)
                        except Exception:
                            write_log(args.log, "Cannot extract file '{}'".format(file_name))

                            return ReturnCode.ERROR

                        path_file = str(path_file)

                        for scan in list_scan:
                            try:
                                virus_found = scan(path_file)
                            except Exception as ex:
                                write_log(args.log, ex)

                                return ReturnCode.ERROR

                            if virus_found is not None:
                                break

                        if virus_found is not None:
                            write_log(args.log, "Virus '{}'".format(virus_found))

                            return ReturnCode.PROBLEM

                        if config.remove_encryption:
                            zf_decrypted.writestr(file_name, data)

                    break
                except RuntimeError:
                    pass
            else:
                write_log(args.log, "Decryption failed")

                return ReturnCode.PROBLEM

    if config.remove_encryption:
        zf_decrypted.close()

        with open(args.input, "wb") as f:
            f.write(buffer.getvalue())

        return ReturnCode.DECRYPTED

    return ReturnCode.SUCCESS
Пример #11
0
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        set_expression = get_expression_list(config.name_expression_list)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if not set_expression:
        write_log(args.log, "Expression list is empty")

        return ReturnCode.ERROR

    part_text = None
    part_html = None

    for part in email.walk():
        if part_text is None and part.get_content_type() == "text/plain":
            part_text = part
        elif part_html is None and part.get_content_type() == "text/html":
            part_html = part

        if part_text is not None and part_html is not None:
            break

    list_pattern = list()

    for expression in set_expression:
        list_pattern.append(re.compile(expression, re.IGNORECASE))

    expression_found = False

    if part_text is not None:
        content_text = part_text.get_payload(decode=True).decode(
            "utf-8", errors="ignore")

        for pattern in list_pattern:
            match = re.search(pattern, content_text)

            if match:
                expression_found = True

                break

    if part_html is not None:
        content_html = part_html.get_payload(decode=True).decode(
            "utf-8", errors="ignore")

        if not expression_found:
            text_html = html2text(content_html)

            for pattern in list_pattern:
                match = re.search(pattern, text_html)

                if match:
                    expression_found = True

                    break

    if expression_found:
        if part_text is not None:
            set_url = {
                url
                for (_, url, _, _) in re.findall(PATTERN_URL, content_text)
            }

            for url in set_url:
                content_text = content_text.replace(url,
                                                    config.url_replacement)

            part_text.set_payload(content_text)

        if part_html is not None:
            soup = BeautifulSoup(content_html, features="html5lib")

            for a in soup.findAll("a", href=True):
                a["href"] = config.url_replacement

            content_html = str(soup)

            part_html.set_payload(content_html)

        try:
            with open(args.input, "wb") as f:
                f.write(email.as_bytes())
        except Exception:
            write_log(args.log, "Error writing '{}'".format(args.input))

            return ReturnCode.ERROR

        return ReturnCode.URL_REPLACED

    return ReturnCode.NOT_MODIFIED
Пример #12
0
def main(args):
    try:
        xml_report = read_file(args.input, ignore_errors=True)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    try:
        syslog.openlog("dmarc_report", facility=syslog.LOG_MAIL)
    except Exception:
        write_log(args.log, "Cannot connect to syslog")

        return ReturnCode.ERROR

    try:
        tree = ElementTree.fromstring(xml_report)
    except Exception:
        write_log(args.log, "Cannot parse xml")

        return ReturnCode.ERROR

    name_org = None
    id_report = None
    date_begin = None
    date_end = None
    domain = None

    for child in tree:
        if child.tag == "report_metadata":
            for grandchild in child:
                if grandchild.tag == "org_name":
                    name_org = grandchild.text
                elif grandchild.tag == "report_id":
                    id_report = grandchild.text
                elif grandchild.tag == "date_range":
                    for grandgrandchild in grandchild:
                        if grandgrandchild.tag == "begin":
                            date_begin = grandgrandchild.text
                        elif grandgrandchild.tag == "end":
                            date_end = grandgrandchild.text
        elif child.tag == "policy_published":
            for grandchild in child:
                if grandchild.tag == "domain":
                    domain = grandchild.text
        elif child.tag == "record":
            for grandchild in child:
                if grandchild.tag == "row":
                    ip_source = None
                    count = None
                    disposition = None
                    dkim = None
                    spf = None

                    for grandgrandchild in grandchild:
                        if grandgrandchild.tag == "source_ip":
                            ip_source = grandgrandchild.text
                        elif grandgrandchild.tag == "count":
                            count = grandgrandchild.text
                        elif grandgrandchild.tag == "policy_evaluated":
                            for grandgrandgrandchild in grandgrandchild:
                                if grandgrandgrandchild.tag == "disposition":
                                    disposition = grandgrandgrandchild.text
                                elif grandgrandgrandchild.tag == "dkim":
                                    dkim = grandgrandgrandchild.text
                                elif grandgrandgrandchild.tag == "spf":
                                    spf = grandgrandgrandchild.text

                    syslog.syslog(
                        TEMPLATE_SYSLOG.substitute(name_org=name_org,
                                                   id_report=id_report,
                                                   date_begin=date_begin,
                                                   date_end=date_end,
                                                   domain=domain,
                                                   ip_source=ip_source,
                                                   count=count,
                                                   disposition=disposition,
                                                   dkim=dkim,
                                                   spf=spf))

                    break

    return ReturnCode.SUCCESS
def main(args):
    try:
        config = get_config(args.config, CONFIG_PARAMETERS)

        email = read_email(args.input)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if "Received" not in email:
        write_log(args.log, "Header received does not exist")

        return ReturnCode.ERROR

    header_received = str(email.get("Received"))

    if not header_received:
        write_log(args.log, "Header received is empty")

        return ReturnCode.ERROR

    sender_ip = re.search(r"\[([0-9.]+)\]", header_received)

    if not sender_ip:
        write_log(args.log, "Cannot find sender IP")

        return ReturnCode.ERROR

    sender_ip = sender_ip.group(1)

    try:
        ipv4_address = IPv4Address(sender_ip)
    except Exception:
        write_log(args.log, "Invalid sender IP")

        return ReturnCode.ERROR

    for network in config.internal_networks:
        try:
            ipv4_network = IPv4Network(network)
        except Exception:
            write_log(args.log, "Invalid CIDR '{}'".format(network))

            return ReturnCode.ERROR

        if ipv4_address in ipv4_network:
            break
    else:
        return ReturnCode.SENDER_EXTERNAL

    if "From" not in email:
        write_log(args.log, "Header does not exist")

        return ReturnCode.ERROR

    header_from = str(email.get("From"))

    if not header_from:
        write_log(args.log, "Header from is empty")

        return ReturnCode.ERROR

    sender_address = extract_email_address(header_from)

    if not sender_address:
        write_log(args.log, "Cannot find sender address")

        return ReturnCode.ERROR

    sender_address = sender_address.group(1)

    index_at = sender_address.find("@")

    if index_at == -1:
        write_log(args.log, "Invalid sender address")

        return ReturnCode.ERROR

    sender_domain = sender_address[index_at + 1:]

    if not sender_domain:
        write_log(args.log, "Empty sender domain")

        return ReturnCode.ERROR

    try:
        set_address = get_address_list(config.name_address_list)
    except Exception as ex:
        write_log(args.log, ex)

        return ReturnCode.ERROR

    if not set_address:
        write_log(args.log, "Address list is empty")

        return ReturnCode.ERROR

    set_domain = {
        email_address[email_address.find("@") + 1:]
        for email_address in set_address
    }

    if sender_domain not in set_domain:
        return ReturnCode.SENDER_EXTERNAL

    return ReturnCode.SENDER_INTERNAL