Exemple #1
0
 def test_check_helo_domain(self):
     for host in self.dynamic_hosts + self.static_hosts:
         params = {
             'client_name': host,
             'helo_name': 'mail.%s' % host,
         }
         self.assertEquals(bleyhelpers.check_helo(params), 1)
Exemple #2
0
 def test_check_helo_bad(self):
     for ip in self.ips:
         params = {
             'client_name': '%s.dyn.example.com' % ip[1],
             'client_address': ipaddr.IPAddress(ip[0]).exploded,
             'helo_name': 'windowsxp.local',
         }
         self.assertEquals(bleyhelpers.check_helo(params), 2)
Exemple #3
0
 def test_check_helo_ip(self):
     for ip in self.ips:
         params = {
             'client_name': '%s.dyn.example.com' % ip[1],
             'client_address': ipaddress.ip_address(six.u(ip[0])).exploded,
             'helo_name': '[%s]' % ip[0],
         }
         self.assertEquals(bleyhelpers.check_helo(params), 1)
Exemple #4
0
    def check_policy(self):
        '''Check the incoming mail based on our policy and tell Postfix
        about our decision.

        The policy works as follows:
          1. Accept if recipient=(postmaster|abuse)
          2. Check local DB for an existing entry
          3. When not found, check
             1. DNSWLs (accept if found)
             2. DNSBLs (reject if found)
             3. HELO/dyn_host/sender_eq_recipient (reject if over threshold)
             4. SPF (reject if over threshold)
             5. Accept if not yet rejected
          4. When found
             1. Whitelisted: accept
             2. Greylisted and waited: accept
             3. Greylisted and not waited: reject

        @type  postfix_params: dict
        @param postfix_params: parameters we got from Postfix
        '''

        if not self.db:
            self.db = self.factory.settings.db
            try:
                self.dbc = self.db.cursor()
            except:
                self.safe_reconnect()

        check_results = {
            'DNSWL': 0,
            'DNSBL': 0,
            'HELO': 0,
            'DYN': 0,
            'DB': -1,
            'SPF': 0,
            'S_EQ_R': 0,
            'WHITELISTED': 0,
            'CACHE': 0
        }
        action = 'DUNNO'
        self.params['now'] = datetime.datetime.now()
        postfix_params = self.params

        # Strip everything after a + in the localpart,
        # usefull for mailinglists etc
        if postfix_params['sender'].find('+') != -1:
            postfix_params['sender'] = postfix_params[
                'sender'][:postfix_params['sender'].
                          find('+')] + postfix_params['sender'][
                              postfix_params['sender'].find('@'):]
        if postfix_params['recipient'].find('+') != -1:
            postfix_params['recipient'] = postfix_params[
                'recipient'][:postfix_params['recipient'].
                             find('+')] + postfix_params['recipient'][
                                 postfix_params['recipient'].find('@'):]

        if postfix_params['client_address'] in self.factory.bad_cache.keys():
            delta = datetime.datetime.now() - self.factory.bad_cache[
                postfix_params['client_address']]
            if delta < datetime.timedelta(0, self.factory.settings.cache_valid,
                                          0):
                action = 'DEFER_IF_PERMIT %s (cached result)' % self.factory.settings.reject_msg
                check_results['CACHE'] = 1
                if self.factory.settings.verbose:
                    logger.info(
                        'decided CACHED action=%s, checks: %s, postfix: %s' %
                        (action, check_results, postfix_params))
                else:
                    logger.info('decided CACHED action=%s, from=%s, to=%s' %
                                (action, postfix_params['sender'],
                                 postfix_params['recipient']))
                self.send_action(action)
                self.factory.log_action(postfix_params, action, check_results)
                return
            else:
                del self.factory.bad_cache[postfix_params['client_address']]

        if postfix_params['client_address'] in self.factory.good_cache.keys():
            delta = datetime.datetime.now() - self.factory.good_cache[
                postfix_params['client_address']]
            if delta < datetime.timedelta(0, self.factory.settings.cache_valid,
                                          0):
                action = 'DUNNO'
                check_results['CACHE'] = 1
                if self.factory.settings.verbose:
                    logger.info(
                        'decided CACHED action=%s, checks: %s, postfix: %s' %
                        (action, check_results, postfix_params))
                else:
                    logger.info('decided CACHED action=%s, from=%s, to=%s' %
                                (action, postfix_params['sender'],
                                 postfix_params['recipient']))
                self.send_action(action)
                self.factory.log_action(postfix_params, action, check_results)
                return
            else:
                del self.factory.good_cache[postfix_params['client_address']]

        status = self.check_local_db(postfix_params)
        # -1 : not found
        #  0 : regular host, not in black, not in white, let it go
        #  1 : regular host, but in white, let it go, dont check EHLO
        #  2 : regular host, but in black, lets grey for now
        if self.check_whitelist(postfix_params['recipient'].lower(),
                                self.factory.settings.whitelist_recipients):
            action = 'DUNNO'
            check_results['WHITELISTED'] = 1
        elif self.check_whitelist(postfix_params['client_name'].lower(),
                                  self.factory.settings.whitelist_clients):
            action = 'DUNNO'
            check_results['WHITELISTED'] = 1
        elif self.check_whitelist_ip(
                postfix_params['client_address'].lower(),
                self.factory.settings.whitelist_clients_ip):
            action = 'DUNNO'
            check_results['WHITELISTED'] = 1
        elif status == -1:  # not found in local db...
            check_results['DNSWL'] = yield self.check_dnswls(
                postfix_params['client_address'],
                self.factory.settings.dnswl_threshold)
            if check_results['DNSWL'] >= self.factory.settings.dnswl_threshold:
                new_status = 1
            else:
                check_results['DNSBL'] = yield self.check_dnsbls(
                    postfix_params['client_address'],
                    self.factory.settings.dnsbl_threshold)
                check_results['HELO'] = bleyhelpers.check_helo(postfix_params)
                check_results['DYN'] = bleyhelpers.check_dyn_host(
                    postfix_params['client_name'])
                # check_sender_eq_recipient:
                if postfix_params['sender'] == postfix_params['recipient']:
                    check_results['S_EQ_R'] = 1
                if self.factory.settings.use_spf and check_results[
                        'DNSBL'] < self.factory.settings.dnsbl_threshold and check_results[
                            'HELO'] + check_results['DYN'] + check_results[
                                'S_EQ_R'] < self.factory.settings.rfc_threshold:
                    check_results['SPF'] = bleyhelpers.check_spf(
                        postfix_params, self.factory.settings.use_spf_guess)
                else:
                    check_results['SPF'] = 0
                if check_results[
                        'DNSBL'] >= self.factory.settings.dnsbl_threshold or check_results[
                            'HELO'] + check_results['DYN'] + check_results[
                                'SPF'] + check_results[
                                    'S_EQ_R'] >= self.factory.settings.rfc_threshold:
                    new_status = 2
                    action = 'DEFER_IF_PERMIT %s' % self.factory.settings.reject_msg
                    self.factory.bad_cache[postfix_params[
                        'client_address']] = datetime.datetime.now()
                else:
                    new_status = 0
                    self.factory.good_cache[postfix_params[
                        'client_address']] = datetime.datetime.now()
            query = "INSERT INTO bley_status (ip, status, last_action, sender, recipient) VALUES(%(client_address)s, %(new_status)s, %(now)s, %(sender)s, %(recipient)s)"
            postfix_params['new_status'] = new_status
            try:
                self.safe_execute(query, postfix_params)
            except:
                # the other thread already commited while we checked, ignore
                pass

        elif status[0] >= 2:  # found to be greyed
            check_results['DB'] = status[0]
            delta = datetime.datetime.now() - status[1]
            if delta > self.factory.settings.greylist_period + status[
                    2] * self.factory.settings.greylist_penalty or delta > self.factory.settings.greylist_max:
                action = 'DUNNO'
                query = "UPDATE bley_status SET status=0, last_action=%(now)s WHERE ip=%(client_address)s AND sender=%(sender)s AND recipient=%(recipient)s"
                self.factory.good_cache[postfix_params[
                    'client_address']] = datetime.datetime.now()
            else:
                action = 'DEFER_IF_PERMIT %s' % self.factory.settings.reject_msg
                query = "UPDATE bley_status SET fail_count=fail_count+1 WHERE ip=%(client_address)s AND sender=%(sender)s AND recipient=%(recipient)s"
                self.factory.bad_cache[postfix_params[
                    'client_address']] = datetime.datetime.now()
            self.safe_execute(query, postfix_params)

        else:  # found to be clean
            check_results['DB'] = status[0]
            action = 'DUNNO'
            query = "UPDATE bley_status SET last_action=%(now)s WHERE ip=%(client_address)s AND sender=%(sender)s AND recipient=%(recipient)s"
            self.safe_execute(query, postfix_params)
            self.factory.good_cache[
                postfix_params['client_address']] = datetime.datetime.now()

        if self.factory.settings.verbose:
            logger.info('decided action=%s, checks: %s, postfix: %s' %
                        (action, check_results, postfix_params))
        else:
            logger.info('decided action=%s, from=%s, to=%s' %
                        (action, postfix_params['sender'],
                         postfix_params['recipient']))
        self.factory.log_action(postfix_params, action, check_results)
        self.send_action(action)
Exemple #5
0
    def check_policy(self):
        '''Check the incoming mail based on our policy and tell Postfix
        about our decision.

        The policy works as follows:
          1. Accept if recipient=(postmaster|abuse)
          2. Check local DB for an existing entry
          3. When not found, check
             1. DNSWLs (accept if found)
             2. DNSBLs (reject if found)
             3. HELO/dyn_host/sender_eq_recipient (reject if over threshold)
             4. SPF (reject if over threshold)
             5. Accept if not yet rejected
          4. When found
             1. Whitelisted: accept
             2. Greylisted and waited: accept
             3. Greylisted and not waited: reject

        @type  postfix_params: dict
        @param postfix_params: parameters we got from Postfix
        '''

        if not self.db:
            self.db = self.factory.settings.db
            try:
                self.dbc = self.db.cursor()
            except:
                self.safe_reconnect()

        check_results = {'DNSWL': 0, 'DNSBL': 0, 'HELO': 0, 'DYN': 0, 'DB': -1,
                         'SPF': 0, 'S_EQ_R': 0, 'WHITELISTED': 0, 'CACHE': 0}
        action = 'DUNNO'
        self.params['now'] = datetime.datetime.now()
        postfix_params = self.params

        # Strip everything after a + in the localpart,
        # usefull for mailinglists etc
        if postfix_params['sender'].find('+') != -1:
            postfix_params['sender'] = postfix_params['sender'][:postfix_params['sender'].find('+')] + postfix_params['sender'][postfix_params['sender'].find('@'):]
        if postfix_params['recipient'].find('+') != -1:
            postfix_params['recipient'] = postfix_params['recipient'][:postfix_params['recipient'].find('+')] + postfix_params['recipient'][postfix_params['recipient'].find('@'):]

        if postfix_params['client_address'] in self.factory.bad_cache.keys():
            delta = datetime.datetime.now() - self.factory.bad_cache[postfix_params['client_address']]
            if delta < datetime.timedelta(0, self.factory.settings.cache_valid, 0):
                action = 'DEFER_IF_PERMIT %s (cached result)' % self.factory.settings.reject_msg
                check_results['CACHE'] = 1
                if self.factory.settings.verbose:
                    logger.info('decided CACHED action=%s, checks: %s, postfix: %s' %
                                (action, check_results, postfix_params))
                else:
                    logger.info('decided CACHED action=%s, from=%s, to=%s' %
                                (action, postfix_params['sender'],
                                 postfix_params['recipient']))
                self.send_action(action)
                self.factory.log_action(postfix_params, action, check_results)
                return
            else:
                del self.factory.bad_cache[postfix_params['client_address']]

        if postfix_params['client_address'] in self.factory.good_cache.keys():
            delta = datetime.datetime.now() - self.factory.good_cache[postfix_params['client_address']]
            if delta < datetime.timedelta(0, self.factory.settings.cache_valid, 0):
                action = 'DUNNO'
                check_results['CACHE'] = 1
                if self.factory.settings.verbose:
                    logger.info('decided CACHED action=%s, checks: %s, postfix: %s' %
                                (action, check_results, postfix_params))
                else:
                    logger.info('decided CACHED action=%s, from=%s, to=%s' %
                                (action, postfix_params['sender'],
                                 postfix_params['recipient']))
                self.send_action(action)
                self.factory.log_action(postfix_params, action, check_results)
                return
            else:
                del self.factory.good_cache[postfix_params['client_address']]

        status = self.check_local_db(postfix_params)
        # -1 : not found
        #  0 : regular host, not in black, not in white, let it go
        #  1 : regular host, but in white, let it go, dont check EHLO
        #  2 : regular host, but in black, lets grey for now
        if self.check_whitelist(postfix_params['recipient'].lower(),
                                self.factory.settings.whitelist_recipients):
            action = 'DUNNO'
            check_results['WHITELISTED'] = 1
        elif self.check_whitelist(postfix_params['client_name'].lower(),
                                  self.factory.settings.whitelist_clients):
            action = 'DUNNO'
            check_results['WHITELISTED'] = 1
        elif self.check_whitelist_ip(postfix_params['client_address'].lower(),
                                     self.factory.settings.whitelist_clients_ip):
            action = 'DUNNO'
            check_results['WHITELISTED'] = 1
        elif status == -1:  # not found in local db...
            check_results['DNSWL'] = yield self.check_dnswls(postfix_params['client_address'], self.factory.settings.dnswl_threshold)
            if check_results['DNSWL'] >= self.factory.settings.dnswl_threshold:
                new_status = 1
            else:
                check_results['DNSBL'] = yield self.check_dnsbls(postfix_params['client_address'], self.factory.settings.dnsbl_threshold)
                check_results['HELO'] = bleyhelpers.check_helo(postfix_params)
                check_results['DYN'] = bleyhelpers.check_dyn_host(postfix_params['client_name'])
                # check_sender_eq_recipient:
                if postfix_params['sender'] == postfix_params['recipient']:
                    check_results['S_EQ_R'] = 1
                if self.factory.settings.use_spf and check_results['DNSBL'] < self.factory.settings.dnsbl_threshold and check_results['HELO'] + check_results['DYN'] + check_results['S_EQ_R'] < self.factory.settings.rfc_threshold:
                    check_results['SPF'] = bleyhelpers.check_spf(postfix_params, self.factory.settings.use_spf_guess)
                else:
                    check_results['SPF'] = 0
                if check_results['DNSBL'] >= self.factory.settings.dnsbl_threshold or check_results['HELO'] + check_results['DYN'] + check_results['SPF'] + check_results['S_EQ_R'] >= self.factory.settings.rfc_threshold:
                    new_status = 2
                    action = 'DEFER_IF_PERMIT %s' % self.factory.settings.reject_msg
                    self.factory.bad_cache[postfix_params['client_address']] = datetime.datetime.now()
                else:
                    new_status = 0
                    self.factory.good_cache[postfix_params['client_address']] = datetime.datetime.now()
            query = "INSERT INTO bley_status (ip, status, last_action, sender, recipient) VALUES(%(client_address)s, %(new_status)s, %(now)s, %(sender)s, %(recipient)s)"
            postfix_params['new_status'] = new_status
            try:
                self.safe_execute(query, postfix_params)
            except:
                # the other thread already commited while we checked, ignore
                pass

        elif status[0] >= 2:  # found to be greyed
            check_results['DB'] = status[0]
            delta = datetime.datetime.now() - status[1]
            if delta > self.factory.settings.greylist_period + status[2] * self.factory.settings.greylist_penalty or delta > self.factory.settings.greylist_max:
                action = 'DUNNO'
                query = "UPDATE bley_status SET status=0, last_action=%(now)s WHERE ip=%(client_address)s AND sender=%(sender)s AND recipient=%(recipient)s"
                self.factory.good_cache[postfix_params['client_address']] = datetime.datetime.now()
            else:
                action = 'DEFER_IF_PERMIT %s' % self.factory.settings.reject_msg
                query = "UPDATE bley_status SET fail_count=fail_count+1 WHERE ip=%(client_address)s AND sender=%(sender)s AND recipient=%(recipient)s"
                self.factory.bad_cache[postfix_params['client_address']] = datetime.datetime.now()
            self.safe_execute(query, postfix_params)

        else:  # found to be clean
            check_results['DB'] = status[0]
            action = 'DUNNO'
            query = "UPDATE bley_status SET last_action=%(now)s WHERE ip=%(client_address)s AND sender=%(sender)s AND recipient=%(recipient)s"
            self.safe_execute(query, postfix_params)
            self.factory.good_cache[postfix_params['client_address']] = datetime.datetime.now()

        if self.factory.settings.verbose:
            logger.info('decided action=%s, checks: %s, postfix: %s' %
                        (action, check_results, postfix_params))
        else:
            logger.info('decided action=%s, from=%s, to=%s' %
                        (action, postfix_params['sender'],
                         postfix_params['recipient']))
        self.factory.log_action(postfix_params, action, check_results)
        self.send_action(action)