Commits

Zhang Huangbin committed 6a6022a

Slightly improve performance in plugin amavisd_block_blacklisted_senders by detecting existence of 'objectClass=amavisAccount'.

  • Participants
  • Parent commits 6962a9a

Comments (0)

Files changed (13)

 iRedAPD is based on [mlapd](http://code.google.com/p/mlapd) which released
 under GPL v2, so iRedAPD is GPL v2 too.
 
-Note: file src/daemon.py is released under its own license, shipped
-for easy deploying.
+Note: file libs/daemon.py is released under its own license (BSD-style
+license), shipped for easy deployment.
 
 # Requirments
 
   shadowLastChange is not present or 0?
 * Query required SQL columns instead of all
 * Plugins:
-    + HELO
+    + HELO restrictions
     + Greylisting (server-wide and per-user, per-domain)
     + White/Blacklisting (server-wide and per-domain)
+    + Require STARTTLS (encryption_protocol=TLSv1)
 * Detect protocol_state=RCPT in smtp session
                 ):
         asynchat.async_chat.__init__(self, conn)
         self.buffer = []
-        self.smtp_session_map = {}
+        self.smtp_session_data = {}
         self.set_terminator('\n')
 
         self.plugins = plugins
             if line.find('=') != -1:
                 key = line.split('=')[0]
                 value = line.split('=', 1)[1]
-                self.smtp_session_map[key] = value
-        elif len(self.smtp_session_map) != 0:
+                self.smtp_session_data[key] = value
+        elif len(self.smtp_session_data) != 0:
             try:
                 modeler = Modeler()
-                result = modeler.handle_data(smtp_session_map=self.smtp_session_map,
+                result = modeler.handle_data(smtp_session_data=self.smtp_session_data,
                                              plugins=self.plugins,
                                              plugins_for_sender=self.plugins_for_sender,
                                              plugins_for_recipient=self.plugins_for_recipient,
                 logging.debug('Unexpected error: %s. Fallback to default action: %s' % (str(e), str(action)))
 
             # Log final action.
-            logging.info('[%s] %s -> %s, %s' % (self.smtp_session_map['client_address'],
-                                                self.smtp_session_map['sender'],
-                                                self.smtp_session_map['recipient'],
+            logging.info('[%s] %s -> %s, %s' % (self.smtp_session_data['client_address'],
+                                                self.smtp_session_data['sender'],
+                                                self.smtp_session_data['recipient'],
                                                 action,
                                                ))
 

File libs/__init__.py

 __author__ = 'Zhang Huangbin <zhb@iredmail.org>'
 __version__ = '1.3.9'
 
-SMTP_ACTIONS = {'accept': 'OK',
-                'defer': 'DEFER_IF_PERMIT Service temporarily unavailable',
-                'reject': 'REJECT Not authorized',
-                'default': 'DUNNO',
-                }
+SMTP_ACTIONS = {
+    'default': 'DUNNO',
+    'accept': 'OK',
+    'reject': 'REJECT Not authorized',
+    'defer': 'DEFER_IF_PERMIT Service temporarily unavailable',
+}
 
 LDAP_ACCESS_POLICIES_OF_MAIL_LIST = {
     'public': 'Unrestricted',

File libs/ldaplib/conn_utils.py

                                      sender,
                                      recipient,
                                      policy):
-    """return search_result_list_based_on_access_policy"""
+    """return list of allowed senders"""
 
     logging.debug('[+] Getting allowed senders of mail list: %s' % recipient)
     recipient_domain = recipient.split('@', 1)[-1]
                     # Example of result data:
                     # [('dn', {'listAllowedUser': ['user@domain.ltd']})]
                     userList += obj[1][k]
-                else:
-                    pass
 
         # Exclude mail list itself.
         if recipient in userList:
         logging.debug('result: %s' % str(userList))
 
         # Query once more to get 'shadowAddress'.
-        if len(userList) > 0 and (policy == 'allowedonly' or policy == 'moderatorsonly'):
+        if len(userList) > 0 and policy in ['allowedonly',
+                                            'moderatorsonly',
+                                            'moderators']:
             logging.debug('Addition query to get user aliases...')
 
             basedn = 'ou=Users,' + domaindn
             except Exception, e:
                 logging.debug('Error: %s' % str(e))
 
-        return userList
+        return [u.lower() for u in userList]
     except Exception, e:
         logging.debug('Error: %s' % str(e))
         return []

File libs/ldaplib/modeler.py

         except Exception, e:
             logging.debug('Error while closing connection: %s' % str(e))
 
-    def handle_data(self, smtp_session_map,
+    def handle_data(self, smtp_session_data,
                     plugins=[],
                     plugins_for_sender=[],
                     plugins_for_recipient=[],
                     plugins_for_misc=[],
-                    sender_search_attrlist=None,
-                    recipient_search_attrlist=None,
-                    ):
+                    sender_search_attrlist=[],
+                    recipient_search_attrlist=[],
+                   ):
         # No sender or recipient in smtp session.
-        if not 'sender' in smtp_session_map or not 'recipient' in smtp_session_map:
+        if not 'sender' in smtp_session_data or not 'recipient' in smtp_session_data:
             return SMTP_ACTIONS['defer']
 
         # Not a valid email address.
-        if len(smtp_session_map['sender']) < 6:
+        if len(smtp_session_data['sender']) < 6:
             return 'DUNNO'
 
         # No plugins available.
             get_recipient_ldif = True
 
         # Get account dn and LDIF data.
-        plugin_kwargs = {'smtpSessionData': smtp_session_map,
+        plugin_kwargs = {'smtp_session_data': smtp_session_data,
                          'conn': self.conn,
-                         'baseDn': settings.ldap_basedn,
-                         'senderDn': None,
-                         'senderLdif': None,
-                         'recipientDn': None,
-                         'recipientLdif': None,
+                         'base_dn': settings.ldap_basedn,
+                         'sender_dn': None,
+                         'sender_ldif': None,
+                         'recipient_dn': None,
+                         'recipient_ldif': None,
                          }
 
         if get_sender_ldif:
             senderDn, senderLdif = conn_utils.get_account_ldif(
                 conn=self.conn,
-                account=smtp_session_map['sender'],
+                account=smtp_session_data['sender'],
                 attrlist=sender_search_attrlist,
             )
-            plugin_kwargs['senderDn'] = senderDn
-            plugin_kwargs['senderLdif'] = senderLdif
+            plugin_kwargs['sender_dn'] = senderDn
+            plugin_kwargs['sender_ldif'] = senderLdif
 
             for plugin in plugins_for_sender:
                 action = conn_utils.apply_plugin(plugin, **plugin_kwargs)
         if get_recipient_ldif:
             recipientDn, recipientLdif = conn_utils.get_account_ldif(
                 conn=self.conn,
-                account=smtp_session_map['recipient'],
+                account=smtp_session_data['recipient'],
                 attrlist=recipient_search_attrlist,
             )
-            plugin_kwargs['recipientDn'] = recipientDn
-            plugin_kwargs['recipientLdif'] = recipientLdif
+            plugin_kwargs['recipient_dn'] = recipientDn
+            plugin_kwargs['recipient_ldif'] = recipientLdif
 
             for plugin in plugins_for_recipient:
                 action = conn_utils.apply_plugin(plugin, **plugin_kwargs)

File libs/sql/modeler.py

         except Exception, e:
             logging.debug('Error while closing connection: %s' % str(e))
 
-    def handle_data(self, smtp_session_map, plugins=[]):
-        if 'sender' in smtp_session_map.keys() and 'recipient' in smtp_session_map.keys():
-            if len(smtp_session_map['sender']) < 6:
+    def handle_data(self, smtp_session_data, plugins=[]):
+        if 'sender' in smtp_session_data.keys() and 'recipient' in smtp_session_data.keys():
+            if len(smtp_session_data['sender']) < 6:
                 # Not a valid email address.
                 return 'DUNNO'
 
             # Sender/recipient are used almost in all plugins, so store them
             # a dict and pass to plugins.
             senderReceiver = {
-                'sender': smtp_session_map['sender'],
-                'recipient': smtp_session_map['recipient'],
-                'sender_domain': smtp_session_map['sender'].split('@')[-1],
-                'recipient_domain': smtp_session_map['recipient'].split('@')[-1],
+                'sender': smtp_session_data['sender'],
+                'recipient': smtp_session_data['recipient'],
+                'sender_domain': smtp_session_data['sender'].split('@')[-1],
+                'recipient_domain': smtp_session_data['recipient'].split('@')[-1],
             }
 
             if len(self.plugins) > 0:
                         pluginAction = module.restriction(
                             dbConn=self.cursor,
                             senderReceiver=senderReceiver,
-                            smtpSessionData=smtp_session_map,
+                            smtp_session_data=smtp_session_data,
                         )
 
                         logging.debug('Response from plugin (%s): %s' % (module.__name__, pluginAction))

File plugins/amavisd_block_blacklisted_senders.py

 
 
 def restriction(**kwargs):
-    smtpSessionData = kwargs['smtpSessionData']
-    ldapRecipientLdif = kwargs['recipientLdif']
-    # Get sender address.
-    sender = smtpSessionData.get('sender').lower()
+    smtp_session_data = kwargs['smtp_session_data']
+    recipient_ldif = kwargs['recipient_ldif']
+
+    if not 'amavisAccount' in recipient_ldif['objectClass']:
+        return 'DUNNO (Not a amavisdAccount object)'
+
+    sender = smtp_session_data.get('sender').lower()
 
     # Get valid Amavisd sender, sender domain and sub-domain(s).
     # - Sample user: user@sub2.sub1.com.cn
         splited_sender_domain.pop(0)
 
     # Get list of amavisBlacklistedSender.
-    blSenders = set([v.lower() for v in ldapRecipientLdif.get('amavisBlacklistSender', [])])
+    blSenders = set([v.lower() for v in recipient_ldif.get('amavisBlacklistSender', [])])
 
     # Get list of amavisWhitelistSender.
-    wlSenders = set([v.lower() for v in ldapRecipientLdif.get('amavisWhitelistSender', [])])
+    wlSenders = set([v.lower() for v in recipient_ldif.get('amavisWhitelistSender', [])])
 
     logging.debug('Sender: %s' % sender)
     logging.debug('Whitelisted senders: %s' % str(wlSenders))

File plugins/ldap_domain_wblist.py

 
 
 def restriction(**kwargs):
-    ldapConn = kwargs['conn']
-    ldapBaseDn = kwargs['baseDn']
-    smtpSessionData = kwargs['smtpSessionData']
+    conn = kwargs['conn']
+    base_dn = kwargs['baseDn']
+    smtp_session_data = kwargs['smtp_session_data']
 
-    sender = smtpSessionData['sender'].lower()
+    sender = smtp_session_data['sender'].lower()
     splitedSenderDomain = str(sender.split('@')[-1]).split('.')
 
     filterOfSenders = ''
     for i in listOfRestrictedSenders:
         filterOfSenders += '(domainWhitelistSender=%s)(domainBlacklistSender=%s)' % (i, i,)
 
-    recipient = smtpSessionData['recipient'].lower()
+    recipient = smtp_session_data['recipient'].lower()
     recipientDomain = recipient.split('@')[-1]
 
     logging.debug('Sender: %s' % sender)
 
     # Query ldap to get domain dn, with domain alias support.
     try:
-        resultDnOfDomain = ldapConn.search_s(
-            ldapBaseDn,
+        resultDnOfDomain = conn.search_s(
+            base_dn,
             1,                  # 1 = ldap.SCOPE_ONELEVEL
             '(|(domainName=%s)(domainAliasName=%s))' % (recipientDomain, recipientDomain),
             ['dn'],
         return 'DUNNO (Error while fetching domain dn: %s)' % (str(e))
 
     # Get list of restricted ip addresses.
-    senderIP = smtpSessionData['client_address']
+    senderIP = smtp_session_data['client_address']
     (ipf1, ipf2, ipf3, ipf4) = senderIP.split('.')
     listOfRestrictedIPAddresses = [
         senderIP,                           # xx.xx.xx.xx
     )
 
     try:
-        resultWblists = ldapConn.search_s(
+        resultWblists = conn.search_s(
             dnOfRecipientDomain,    # Base dn.
             0,                      # Search scope. 0 = ldap.SCOPE_BASE
             filter,                 # Search filter.

File plugins/ldap_maillist_access_policy.py

 SENDER_SEARCH_ATTRLIST = []
 RECIPIENT_SEARCH_ATTRLIST = ['accessPolicy']
 
+
 def restriction(**kwargs):
-    smtpSessionData = kwargs['smtpSessionData']
-    ldapConn = kwargs['conn']
-    ldapBaseDn = kwargs['baseDn']
-    ldapRecipientDn = kwargs['recipientDn']
-    ldapRecipientLdif = kwargs['recipientLdif']
+    smtp_session_data = kwargs['smtp_session_data']
+    conn = kwargs['conn']
+    base_dn = kwargs['base_dn']
+    recipient_dn = kwargs['recipient_dn']
+    recipient_ldif = kwargs['recipient_ldif']
 
     # Return if recipient is not a mail list object.
-    if 'maillist' not in [v.lower() for v in ldapRecipientLdif['objectClass']]:
+    if not 'mailList' in recipient_ldif['objectClass']:
         return 'DUNNO (Not mail list)'
 
-    sender = smtpSessionData['sender'].lower()
-    sender_domain = sender.split('@')[-1]
-
-    recipient = smtpSessionData['recipient'].lower()
-    recipient_domain = recipient.split('@')[-1]
+    sender = smtp_session_data['sender'].lower()
+    recipient = smtp_session_data['recipient'].lower()
     recipient_alias_domains = []
 
-    policy = ldapRecipientLdif.get('accessPolicy', ['public'])[0].lower()
+    policy = recipient_ldif.get('accessPolicy', ['public'])[0].lower()
 
-    if policy in ['domain', 'subdomain',]:
-        try:
-            qr = ldapConn.search_s(
-                ldapBaseDn,
-                1, # 1 == ldap.SCOPE_ONELEVEL
-                "(&(objectClass=mailDomain)(|(domainName=%s)(domainAliasName=%s)))" % (recipient_domain, recipient_domain),
-                ['domainName', 'domainAliasName',]
-            )
-            if len(qr) > 0:
-                recipient_alias_domains = qr[0][1].get('domainName', []) + qr[0][1].get('domainAliasName', [])
-        except Exception, e:
-            logging.debug('Error while fetch domainAliasName: %s' % str(e))
-
-        logging.debug('Recipient domain and alias domains: %s' % ','.join(recipient_alias_domains))
-
+    # Log access policy and description
     logging.debug('%s -> %s, access policy: %s (%s)' % (
         sender, recipient, policy,
         LDAP_ACCESS_POLICIES_OF_MAIL_LIST.get(policy, 'no description'))
     )
 
+    if policy in ['domain', 'subdomain', ]:
+        recipient_domain = recipient.split('@', 1)[-1]
+        try:
+            qr = conn.search_s(
+                base_dn,
+                1, # 1 == ldap.SCOPE_ONELEVEL
+                "(&(objectClass=mailDomain)(|(domainName=%s)(domainAliasName=%s)))" % (recipient_domain, recipient_domain),
+                ['domainName', 'domainAliasName', ]
+            )
+            if qr:
+                dn, entries = qr[0]
+                recipient_alias_domains = \
+                        entries.get('domainName', []) + \
+                        entries.get('domainAliasName', [])
+        except Exception, e:
+            logging.debug('Error while fetching alias domains: %s' % str(e))
+
+        logging.debug('Recipient domain and alias domains: %s' % ','.join(recipient_alias_domains))
+
+    # Verify access policy
     if policy == 'public':
         # No restriction.
         return 'DUNNO (Access policy: public)'
     elif policy == "domain":
+        sender_domain = sender.split('@', 1)[-1]
         # Bypass all users under the same domain.
         if sender_domain in recipient_alias_domains:
             return 'DUNNO (Access policy: domain)'
 
         if returned is False:
             return SMTP_ACTIONS['reject']
-    else:
+    elif policy in ['membersonly', 'members',
+                    'moderatorsonly', 'moderators',
+                    'allowedonly', 'membersandmoderatorsonly']:
         # Handle other access policies: membersOnly, allowedOnly, membersAndModeratorsOnly.
         allowedSenders = conn_utils.get_allowed_senders_of_mail_list(
-            conn=ldapConn,
-            base_dn=ldapBaseDn,
-            dn_of_mail_list=ldapRecipientDn,
+            conn=conn,
+            base_dn=base_dn,
+            dn_of_mail_list=recipient_dn,
             sender=sender,
             recipient=recipient,
             policy=policy,
         )
 
-        if sender.lower() in [v.lower() for v in allowedSenders]:
+        if sender in allowedSenders:
             return 'DUNNO (Sender is allowed)'
         else:
             return SMTP_ACTIONS['reject']
+    else:
+        # Unknown access policy
+        return SMTP_ACTIONS['default']

File plugins/ldap_recipient_restrictions.py

 
 def restriction(**kwargs):
     ldapSenderLdif = kwargs['senderLdif']
-    smtpSessionData = kwargs['smtpSessionData']
+    smtp_session_data = kwargs['smtp_session_data']
 
     # Get recipient address.
-    smtpRecipient = smtpSessionData.get('recipient').lower()
+    smtpRecipient = smtp_session_data.get('recipient').lower()
     splited_recipient_domain = str(smtpRecipient.split('@')[-1]).split('.')
 
     # Get correct domain name and sub-domain name.

File plugins/sql_alias_access_policy.py

 POLICY_ALLOWEDONLY = 'allowedonly'      # Same as @POLICY_MODERATORSONLY
 POLICY_MEMBERSANDMODERATORSONLY = 'membersandmoderatorsonly'
 
-def restriction(dbConn, senderReceiver, smtpSessionData, **kargs):
+def restriction(dbConn, senderReceiver, smtp_session_data, **kargs):
 
     sql = '''SELECT accesspolicy, goto, moderators
             FROM alias

File plugins/sql_user_restrictions.py

 REQUIRE_LOCAL_SENDER = False
 REQUIRE_LOCAL_RECIPIENT = False
 
-def restriction(dbConn, senderReceiver, smtpSessionData, **kargs):
+def restriction(dbConn, senderReceiver, smtp_session_data, **kargs):
     # Get restriction rules for sender
     sql = '''
         SELECT