Advertisement
Guest User

Untitled

a guest
Jul 22nd, 2016
291
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.42 KB | None | 0 0
  1. # Author: Zhang Huangbin <zhb _at_ iredmail.org>
  2. # Purpose: Reject senders listed in per-user blacklists, bypass senders listed
  3. # in per-user whitelists stored in Amavisd database (@lookup_sql_dsn).
  4. #
  5. # Note: Amavisd is configured to be an after-queue content filter in iRedMail.
  6. # with '@lookup_sql_dsn' setting enabled in Amavisd config file, Amavisd
  7. # will query per-recipient, per-domain and server-wide (a.k.a. catch-all)
  8. # white/blacklists and policy rules (tables: `mailaddr`, `users`,
  9. # `wblist`, `policy`) stored in Amavisd SQL database.
  10. #
  11. # if you don't enable this plugin, Amavisd will quarantine emails sent
  12. # from blacklisted senders, and bypass spam scanning for emails sent from
  13. # whitelisted senders (note: other checkings like banned filename, bad
  14. # headers, virus are still checked - if you didn't disable them in
  15. # `amavisd.policy`). With this plugin, we can tell Postfix to reject
  16. # blacklisted sender BEFORE email enter mail queue, or bypass emails sent
  17. # from whitelisted senders directly.
  18. #
  19. # How to use this plugin:
  20. #
  21. # *) Enable `@lookup_sql_dsn` with correct SQL account credential in Amavisd
  22. # config file.
  23. #
  24. # *) Set Amavisd lookup SQL database related parameters (`amavisd_db_*`) in
  25. # iRedAPD config file `/opt/iredapd/settings.py`.
  26. #
  27. # *) Enable this plugin in iRedAPD config file `/opt/iredapd/settings.py`,
  28. # parameter `plugins =`.
  29. #
  30. # *) Restart iRedAPD service.
  31. #
  32. # Formats of valid white/blacklist senders:
  33. #
  34. # - user@domain.com: single sender email address
  35. # - @domain.com: entire sender domain
  36. # - @.domain.com: entire sender domain and all sub-domains
  37. # - @.: all senders
  38. # - 192.168.1.2: single sender ip address
  39. # - 192.168.1.*, 192.168.*.2: wildcard sender ip addresses.
  40. # NOTE: if you want to use
  41. # wildcard IP address like '192.*.1.2', '192.*.*.2', please
  42. # set 'WBLIST_ENABLE_ALL_WILDCARD_IP = True' in
  43. # /opt/iredapd/settings.py.
  44.  
  45. from libs.logger import logger
  46. from libs import SMTP_ACTIONS
  47. from libs.utils import is_ipv4, wildcard_ipv4, sqllist
  48. from libs.amavisd import core as amavisd_lib
  49. import settings
  50.  
  51. REQUIRE_AMAVISD_DB = True
  52.  
  53. if settings.backend == 'ldap':
  54. from libs.ldaplib.conn_utils import is_local_domain
  55. else:
  56. from libs.sql import is_local_domain
  57.  
  58.  
  59. def query_external_addresses(conn, addresses):
  60. '''Return list of `mailaddr.id` of external addresses.'''
  61.  
  62. # Get 'mailaddr.id' of external addresses, ordered by priority
  63. sql = """SELECT id, email
  64. FROM mailaddr
  65. WHERE email IN %s
  66. ORDER BY priority DESC""" % sqllist(addresses)
  67. logger.debug('[SQL] Query external addresses: \n%s' % sql)
  68.  
  69. qr = conn.execute(sql)
  70. qr_addresses = qr.fetchall()
  71. ids = []
  72. if qr_addresses:
  73. ids = [r.id for r in qr_addresses]
  74.  
  75. if not ids:
  76. # don't waste time if we don't even have senders stored in sql db.
  77. logger.debug('No record found in SQL database.')
  78. return []
  79. else:
  80. logger.debug('Addresses (in `mailaddr`): %s' % str(qr_addresses))
  81. return ids
  82.  
  83.  
  84. def query_local_addresses(conn, addresses):
  85. '''Return list of `users.id` of local addresses.'''
  86.  
  87. # Get 'users.id' of local addresses
  88. sql = """SELECT id, email
  89. FROM users
  90. WHERE email IN %s
  91. ORDER BY priority DESC""" % sqllist(addresses)
  92. logger.debug('[SQL] Query local addresses: \n%s' % sql)
  93.  
  94. qr = conn.execute(sql)
  95. qr_addresses = qr.fetchall()
  96. ids = []
  97. if qr_addresses:
  98. ids = [r.id for r in qr_addresses]
  99.  
  100. if not ids:
  101. # don't waste time if we don't have any per-recipient wblist.
  102. logger.debug('No record found in SQL database.')
  103. return []
  104. else:
  105. logger.debug('Local addresses (in `users`): %s' % str(qr_addresses))
  106. return ids
  107.  
  108.  
  109. def apply_wblist_on_inbound(conn, sender_ids, recipient_ids):
  110. # Return if no valid sender or recipient id.
  111. if not (sender_ids and recipient_ids):
  112. logger.debug('No valid sender id or recipient id.')
  113. return SMTP_ACTIONS['default']
  114.  
  115. # Get wblist
  116. sql = """SELECT rid, sid, wb
  117. FROM wblist
  118. WHERE sid IN %s AND rid IN %s""" % (sqllist(sender_ids), sqllist(recipient_ids))
  119. logger.debug('[SQL] Query inbound wblist (in `wblist`): \n%s' % sql)
  120. qr = conn.execute(sql)
  121. wblists = qr.fetchall()
  122.  
  123. if not wblists:
  124. # no wblist
  125. logger.debug('No wblist found.')
  126. return SMTP_ACTIONS['default']
  127.  
  128. logger.debug('Found inbound wblist: %s' % str(wblists))
  129.  
  130. # Check sender addresses
  131. # rids/recipients are orded by priority
  132. for rid in recipient_ids:
  133. # sids/senders are sorted by priority
  134. for sid in sender_ids:
  135. if (rid, sid, 'W') in wblists:
  136. return SMTP_ACTIONS['accept'] + " wblist=(%d, %d, 'W')" % (rid, sid)
  137.  
  138. if (rid, sid, 'B') in wblists:
  139. logger.info("Blacklisted: wblist=(%d, %d, 'B')" % (rid, sid))
  140. return SMTP_ACTIONS['reject_blacklisted']
  141.  
  142. return SMTP_ACTIONS['default']
  143.  
  144.  
  145. def apply_wblist_on_outbound(conn, sender_ids, recipient_ids):
  146. # Return if no valid sender or recipient id.
  147. if not (sender_ids and recipient_ids):
  148. logger.debug('No valid sender id or recipient id.')
  149. return SMTP_ACTIONS['default']
  150.  
  151. # Bypass outgoing emails.
  152. if settings.WBLIST_BYPASS_OUTGOING_EMAIL:
  153. logger.debug('Bypass outgoing email as defined in WBLIST_BYPASS_OUTGOING_EMAIL.')
  154. return SMTP_ACTIONS['default']
  155.  
  156. # Get wblist
  157. sql = """SELECT rid, sid, wb
  158. FROM outbound_wblist
  159. WHERE sid IN %s AND rid IN %s""" % (sqllist(sender_ids), sqllist(recipient_ids))
  160. logger.debug('[SQL] Query outbound wblist: \n%s' % sql)
  161. qr = conn.execute(sql)
  162. wblists = qr.fetchall()
  163.  
  164. if not wblists:
  165. # no wblist
  166. logger.debug('No wblist found.')
  167. return SMTP_ACTIONS['default']
  168.  
  169. logger.debug('Found outbound wblist: %s' % str(wblists))
  170.  
  171. # Check sender addresses
  172. # rids/recipients are orded by priority
  173. for sid in sender_ids:
  174. for rid in recipient_ids:
  175. if (rid, sid, 'W') in wblists:
  176. return SMTP_ACTIONS['accept'] + " outbound_wblist=(%d, %d, 'W')" % (rid, sid)
  177.  
  178. if (rid, sid, 'B') in wblists:
  179. logger.info("Blacklisted: outbound_wblist=(%d, %d, 'B')" % (rid, sid))
  180. return SMTP_ACTIONS['reject_blacklisted']
  181.  
  182. return SMTP_ACTIONS['default']
  183.  
  184.  
  185. def restriction(**kwargs):
  186. conn = kwargs['conn_amavisd']
  187. conn_vmail = kwargs['conn_vmail']
  188.  
  189. if not conn:
  190. logger.error('Error, no valid Amavisd database connection.')
  191. return SMTP_ACTIONS['default']
  192.  
  193. # Get sender
  194. sender = kwargs['sender']
  195. sender_domain = kwargs['sender_domain']
  196. if kwargs['sasl_username']:
  197. # Use sasl_username as sender for outgoing email
  198. sender = kwargs['sasl_username']
  199. sender_domain = kwargs['sasl_username_domain']
  200.  
  201. if not sender:
  202. logger.debug('Bypass: both sender and sasl_username are empty.')
  203. return SMTP_ACTIONS['default']
  204.  
  205. recipient = kwargs['recipient']
  206.  
  207. if sender == recipient:
  208. logger.debug('Sender is same as recipient, bypassed.')
  209. return SMTP_ACTIONS['default']
  210.  
  211. valid_senders = amavisd_lib.get_valid_addresses_from_email(sender)
  212. valid_recipients = amavisd_lib.get_valid_addresses_from_email(recipient)
  213.  
  214. if not kwargs['sasl_username']:
  215. # Sender 'username@*'
  216. sender_username = sender.split('@', 1)[0]
  217. if '+' in sender_username:
  218. valid_senders.append(sender_username.split('+', 1)[0] + '@*')
  219. else:
  220. valid_senders.append(sender_username + '@*')
  221.  
  222. # Append original IP address and all possible wildcast IP addresses
  223. client_address = kwargs['client_address']
  224.  
  225. valid_senders.append(client_address)
  226. if is_ipv4(client_address):
  227. valid_senders += wildcard_ipv4(client_address)
  228.  
  229. logger.debug('Possible policy senders: %s' % str(valid_senders))
  230. logger.debug('Possible policy recipients: %s' % str(valid_recipients))
  231.  
  232. if kwargs['sasl_username'] or is_local_domain(conn=conn_vmail, domain=sender_domain):
  233. logger.debug('Apply wblist for outbound message.')
  234.  
  235. id_of_ext_addresses = []
  236. id_of_local_addresses = query_local_addresses(conn, valid_senders)
  237. if id_of_local_addresses:
  238. id_of_ext_addresses = query_external_addresses(conn, valid_recipients)
  239.  
  240. return apply_wblist_on_outbound(conn,
  241. sender_ids=id_of_local_addresses,
  242. recipient_ids=id_of_ext_addresses)
  243. else:
  244. logger.debug('Apply wblist for inbound message.')
  245.  
  246. id_of_ext_addresses = []
  247. id_of_local_addresses = query_local_addresses(conn, valid_recipients)
  248. if id_of_local_addresses:
  249. id_of_ext_addresses = query_external_addresses(conn, valid_senders)
  250.  
  251. return apply_wblist_on_inbound(conn,
  252. sender_ids=id_of_ext_addresses,
  253. recipient_ids=id_of_local_addresses)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement