Example #1
0
def get_account_outbound_wblist(conn,
                                account,
                                whitelist=True,
                                blacklist=True):
    """Get outbound white/blacklists of specified account."""
    sql_where = 'users.email=%s AND users.id=outbound_wblist.sid AND outbound_wblist.rid = mailaddr.id' % sqlquote(account)
    if whitelist and not blacklist:
        sql_where += " AND outbound_wblist.wb='W'"

    if not whitelist and blacklist:
        sql_where += " AND outbound_wblist.wb='B'"

    wl = []
    bl = []

    try:
        sql = """SELECT mailaddr.email, outbound_wblist.wb
                   FROM mailaddr, users, outbound_wblist
                  WHERE %s""" % sql_where
        qr = conn.execute(sql)
        sql_records = qr.fetchall()

        for r in sql_records:
            (_addr, _wb) = r
            if _wb == 'W':
                wl.append(utils.bytes2str(_addr))
            else:
                bl.append(utils.bytes2str(_addr))
    except Exception as e:
        return (False, e)

    return (True, {'whitelist': wl, 'blacklist': bl})
Example #2
0
def __get_primary_and_alias_domains(domain, with_primary_domain=True, conn=None):
    """Get list of domainName and domainAliasName by quering domainName.

    >>> __get_primary_and_alias_domains(domain='example.com')
    (True, ['example.com', 'aliasdomain01.com', 'aliasdomain02.com', ...])
    """
    domain = domain.strip().lower()
    if not utils.is_domain(domain):
        return (False, 'INVALID_DOMAIN_NAME')

    try:
        if not conn:
            _wrap = LDAPWrap()
            conn = _wrap.conn

        dn = 'domainName=%s,%s' % (domain, settings.iredmail_ldap_basedn)
        qr = conn.search_s(dn,
                           ldap.SCOPE_BASE,
                           '(&(objectClass=mailDomain)(domainName=%s))' % domain,
                           ['domainName', 'domainAliasName'])

        if qr:
            (_dn, _ldif) = qr[0]
            _ldif = utils.bytes2str(_ldif)
            all_domains = _ldif.get('domainName', []) + _ldif.get('domainAliasName', [])
            if not with_primary_domain:
                all_domains.remove(domain)
            return (True, all_domains)
        else:
            return (False, 'INVALID_DOMAIN_NAME')
    except Exception as e:
        return (False, repr(e))
Example #3
0
def get_primary_and_alias_domains(conn, domain):
    """Query LDAP to get all available alias domain names of given domain.

    Return list of alias domain names.

    @conn -- ldap connection cursor
    @domain -- domain name
    """
    if not utils.is_domain(domain):
        return []

    try:
        _f = "(&(objectClass=mailDomain)(|(domainName={})(domainAliasName={})))".format(domain, domain)
        qr = conn.search_s(settings.ldap_basedn,
                           1,  # 1 == ldap.SCOPE_ONELEVEL
                           _f,
                           ['domainName', 'domainAliasName'])
        if qr:
            (_dn, _ldif) = qr[0]
            _ldif = utils.bytes2str(_ldif)
            _all_domains = _ldif.get('domainName', []) + _ldif.get('domainAliasName', [])

            return list(set(_all_domains))
    except Exception as e:
        # Log and return if LDAP error occurs
        logger.error("Error while querying alias domains of domain ({}): {}".format(domain, repr(e)))
        return []
Example #4
0
def get_id_of_possible_cidr_network(conn, client_address):
    """Return list of `mailaddr.id` which are CIDR network addresses."""
    ids = []

    if not client_address:
        logger.debug("No client address.")
        return ids

    try:
        _ip = ipaddress.ip_address(client_address)
        if _ip.version == 4:
            first_field = client_address.split(".")[0]
            sql_cidr = first_field + r".%%"
        else:
            return ids
    except:
        return ids

    sql = """SELECT id, email
               FROM mailaddr
              WHERE email LIKE %s
           ORDER BY priority DESC""" % sqlquote(sql_cidr)
    logger.debug("[SQL] Query CIDR network: \n{}".format(sql))

    try:
        qr = conn.execute(sql)
        qr_cidr = qr.fetchall()
    except Exception as e:
        logger.error("Error while querying CIDR network: {}, SQL: \n{}".format(
            repr(e), sql))
        return ids

    if qr_cidr:
        _cidrs = [(int(r.id), utils.bytes2str(r.email)) for r in qr_cidr]

        # Get valid CIDR.
        _ip_networks = set()
        for (_id, _cidr) in _cidrs:
            # Verify whether client_address is in CIDR network
            try:
                _net = ipaddress.ip_network(_cidr)
                _ip_networks.add((_id, _net))
            except:
                pass

        if _ip_networks:
            _ip = ipaddress.ip_address(client_address)
            for (_id, _net) in _ip_networks:
                if _ip in _net:
                    ids.append(_id)

    logger.debug("IDs of CIDR network(s): {}".format(ids))
    return ids
Example #5
0
def get_account_ldif(conn, account, query_filter=None, attrs=None):
    logger.debug("[+] Getting LDIF data of account: {}".format(account))

    if not query_filter:
        query_filter = '(&' + \
                       '(!(domainStatus=disabled))' + \
                       '(|(mail={account})(shadowAddress={account}))'.format(account=account) + \
                       '(|' + \
                       '(objectClass=mailUser)' + \
                       '(objectClass=mailList)' + \
                       '(objectClass=mailAlias)' + \
                       '))'

    logger.debug("search: base_dn={}, scope=SUBTREE, filter={}, "
                 "attributes={}".format(
                     settings.ldap_basedn,
                     query_filter,
                     attrs))

    if not isinstance(attrs, list):
        # Attribute list must be None (search all attributes) or non-empty list
        attrs = None

    try:
        result = conn.search_s(settings.ldap_basedn,
                               ldap.SCOPE_SUBTREE,
                               query_filter,
                               attrs)

        if result:
            logger.debug("result: {}".format(repr(result)))
            (_dn, _ldif) = result[0]
            _ldif = utils.bytes2str(_ldif)
            return (_dn, _ldif)
        else:
            logger.debug('No such account.')
            return (None, None)
    except Exception as e:
        logger.debug("<!> ERROR: {}".format(repr(e)))
        return (None, None)
Example #6
0
def get_existing_maillists(domains=None, conn=None):
    """Get existing mailing lists.

    :param domains: a list/tuple/set of valid domain names.
                    Used if you want to get mailing lists under given domains.
    :param conn: sql connection cursor.
    """
    if domains:
        domains = [str(d).lower() for d in domains if utils.is_domain(d)]

    if not conn:
        _wrap = LDAPWrap()
        conn = _wrap.conn

    _filter = '(&(objectClass=mailList)(enabledService=mlmmj))'
    if domains:
        _f = '(|'
        for d in domains:
            _f += '(mail=*@%s)(shadowAddress=*@%s)' % (d, d)
        _f += ')'
        _filter = '(&' + _filter + _f + ')'

    existing_lists = set()
    try:
        qr = conn.search_s(settings.iredmail_ldap_basedn,
                           ldap.SCOPE_SUBTREE,
                           _filter,
                           ['mail'])

        for (_dn, _ldif) in qr:
            _ldif = utils.bytes2str(_ldif)
            _addresses = _ldif.get('mail', [])
            _addresses = [str(i).lower() for i in _addresses]
            existing_lists.update(_addresses)

        return (True, existing_lists)
    except Exception as e:
        return (False, repr(e))
Example #7
0
def get_alias_target_domain(alias_domain, conn, include_backupmx=True):
    """Query target domain of given alias domain name."""
    alias_domain = str(alias_domain).lower()

    if not utils.is_domain(alias_domain):
        logger.debug("Given alias_domain {} is not an valid domain name.".format(alias_domain))
        return None

    try:
        _filter = '(&(objectClass=mailDomain)(accountStatus=active)'
        _filter += '(domainAliasName=%s)' % alias_domain

        if not include_backupmx:
            _filter += '(!(domainBackupMX=yes))'

        _filter += ')'

        logger.debug("[LDAP] query target domain of given alias domain: {}\n"
                     "[LDAP] query filter: {}".format(alias_domain, _filter))
        qr = conn.search_s(settings.ldap_basedn,
                           1,   # 1 == ldap.SCOPE_ONELEVEL
                           _filter,
                           ['domainName'])

        logger.debug("result: {}".format(repr(qr)))
        if qr:
            (_dn, _ldif) = qr[0]
            _ldif = utils.bytes2str(_ldif)
            _domain = _ldif['domainName'][0]
            return _domain
    except ldap.NO_SUCH_OBJECT:
        pass
    except Exception as e:
        logger.error("<!> Error while querying alias domain: {}".format(repr(e)))

    return None
Example #8
0
args = sys.argv[1:]
if "-A" in args:
    # Query db to get all mailing lists.
    if backend == "sql":
        qr = conn.select("maillists", what="address", where="active=1")
        for i in qr:
            addr = i["address"].lower()
            mls.append(addr)
    elif backend == "ldap":
        _filter = "(&(objectClass=mailList)(accountStatus=active)(enabledService=mlmmj))"
        try:
            qr = conn.search_s(settings.iredmail_ldap_basedn,
                               ldap.SCOPE_SUBTREE, _filter, ["mail"])
            for (_dn, _ldif) in qr:
                mls += [bytes2str(i).lower() for i in _ldif["mail"]]
        except Exception as e:
            print("Error while querying: {}".format(repr(e)))

    if not mls:
        print("No mailing list found. Abort.")
        sys.exit()
else:
    # Get domain names and mailing lists.
    domains = [i.lower() for i in args if is_domain(i)]
    mls = [i.lower() for i in args if is_email(i)]

    # Query SQL/LDAP to get all mailing lists under specified domains.
    if domains:
        print("Querying mailing lists under given domain(s): {}".format(
            ", ".join(domains)))
Example #9
0
def add_maillist(mail, form, conn=None):
    """Add required LDAP object to add a mailing list account."""
    mail = str(mail).lower()
    (_, domain) = mail.split('@', 1)

    if not utils.is_email(mail):
        return (False, 'INVALID_EMAIL')

    if not conn:
        _wrap = LDAPWrap()
        conn = _wrap.conn

    if not is_domain_exists(domain=domain):
        return (False, 'NO_SUCH_DOMAIN')

    if is_email_exists(mail=mail):
        return (False, 'ALREADY_EXISTS')

    name = form.get('name', '')
    mlid = __get_new_mlid(conn=conn)

    domain_status = 'disabled'
    alias_domains = []

    dn_domain = 'domainName=%s,%s' % (domain, settings.iredmail_ldap_basedn)
    # Get domain profile.
    try:
        qr = conn.search_s(dn_domain,
                           ldap.SCOPE_BASE,
                           '(objectClass=*)')

        if qr:
            (_dn, _ldif) = qr[0]
            _ldif = utils.bytes2str(_ldif)
            domain_status = _ldif.get('accountStatus', ['active'])[0]

            alias_domains = _ldif.get('domainAliasName', [])
            alias_domains = [str(i).lower() for i in alias_domains if utils.is_domain(i)]
            alias_domains = list(set(alias_domains))

        if 'only_moderator_can_post' in form:
            access_policy = 'moderatorsonly'
        elif 'only_subscriber_can_post' in form:
            access_policy = 'moderatorsonly'
        else:
            access_policy = None

        max_message_size = form_utils.get_max_message_size(form)

        moderators = form.get('moderators', '').split(',')

        dn_ml = 'mail=%s,ou=Groups,domainName=%s,%s' % (mail, domain, settings.iredmail_ldap_basedn)
        ldif_ml = __ldif_ml(mail=mail,
                            mlid=mlid,
                            name=name,
                            access_policy=access_policy,
                            max_message_size=max_message_size,
                            alias_domains=alias_domains,
                            domain_status=domain_status,
                            moderators=moderators)

        conn.add_s(dn_ml, ldif_ml)
        logger.info('Created: {0}.'.format(mail))
        return (True,)
    except Exception as e:
        logger.error('Error while creating {0}: {1}'.format(mail, e))
        return (False, repr(e))
Example #10
0
def add_wblist(conn,
               account,
               wl_senders=None,
               bl_senders=None,
               wl_rcpts=None,
               bl_rcpts=None,
               flush_before_import=False):
    """Add white/blacklists for specified account.

    wl_senders -- whitelist senders (inbound)
    bl_senders -- blacklist senders (inbound)
    wl_rcpts -- whitelist recipients (outbound)
    bl_rcpts -- blacklist recipients (outbound)
    flush_before_import -- Delete all existing wblist before importing
                           new wblist
    """
    if not utils.is_valid_amavisd_address(account):
        return (False, 'INVALID_ACCOUNT')

    # Remove duplicate.
    if wl_senders:
        wl_senders = {str(s).lower()
                      for s in wl_senders
                      if utils.is_valid_amavisd_address(s)}
    else:
        wl_senders = set()

    # Whitelist has higher priority, don't include whitelisted sender.
    if bl_senders:
        bl_senders = {str(s).lower()
                      for s in bl_senders
                      if utils.is_valid_amavisd_address(s)}
    else:
        bl_senders = set()

    if wl_rcpts:
        wl_rcpts = {str(s).lower()
                    for s in wl_rcpts
                    if utils.is_valid_amavisd_address(s)}
    else:
        wl_rcpts = set()

    if bl_rcpts:
        bl_rcpts = {str(s).lower()
                    for s in bl_rcpts
                    if utils.is_valid_amavisd_address(s)}
    else:
        bl_rcpts = set()

    if flush_before_import:
        if wl_senders:
            bl_senders = {s for s in bl_senders if s not in wl_senders}

        if wl_rcpts:
            bl_rcpts = {s for s in bl_rcpts if s not in wl_rcpts}

    sender_addresses = set(wl_senders) | set(bl_senders)
    rcpt_addresses = set(wl_rcpts) | set(bl_rcpts)
    all_addresses = list(sender_addresses | rcpt_addresses)

    # Get current user's id from `amavisd.users`
    qr = get_user_record(conn=conn, account=account)

    if qr[0]:
        user_id = qr[1]['id']
    else:
        return qr

    # Delete old records
    if flush_before_import:
        # user_id = wblist.rid
        conn.execute('DELETE FROM wblist WHERE rid=%s' % sqlquote(user_id))

        # user_id = outbound_wblist.sid
        conn.execute('DELETE FROM outbound_wblist WHERE sid=%s' % sqlquote(user_id))

    if not all_addresses:
        return (True, )

    # Insert all senders into `amavisd.mailaddr`
    create_mailaddr(conn=conn, addresses=all_addresses)

    # Get `mailaddr.id` of senders
    sender_records = {}
    if sender_addresses:
        sql = "SELECT id, email FROM mailaddr WHERE email IN %s" % sqlquote(list(sender_addresses))
        qr = conn.execute(sql)
        sql_records = qr.fetchall()
        for r in sql_records:
            (_id, _email) = r
            sender_records[utils.bytes2str(_email)] = int(_id)
        del qr

    # Get `mailaddr.id` of recipients
    rcpt_records = {}
    if rcpt_addresses:
        sql = "SELECT id, email FROM mailaddr WHERE email IN %s" % sqlquote(list(rcpt_addresses))
        qr = conn.execute(sql)
        sql_records = qr.fetchall()

        for r in sql_records:
            (_id, _email) = r
            rcpt_records[utils.bytes2str(_email)] = int(_id)
        del qr

    # Remove existing records of current submitted records then insert new.
    try:
        if sender_records:
            sql = "DELETE FROM wblist WHERE rid=%d AND sid IN %s" % (user_id, sqlquote(list(sender_records.values())))
            conn.execute(sql)

        if rcpt_records:
            sql = "DELETE FROM outbound_wblist WHERE sid=%d AND rid IN %s" % (user_id, sqlquote(list(rcpt_records.values())))
            conn.execute(sql)
    except Exception as e:
        return (False, e)

    # Generate dict used to build SQL statements for importing wblist
    values = []
    if sender_addresses:
        for s in wl_senders:
            if sender_records.get(s):
                values.append({'rid': user_id, 'sid': sender_records[s], 'wb': 'W'})

        for s in bl_senders:
            # Filter out same record in blacklist
            if sender_records.get(s) and s not in wl_senders:
                values.append({'rid': user_id, 'sid': sender_records[s], 'wb': 'B'})

    rcpt_values = []
    if rcpt_addresses:
        for s in wl_rcpts:
            if rcpt_records.get(s):
                rcpt_values.append({'sid': user_id, 'rid': rcpt_records[s], 'wb': 'W'})

        for s in bl_rcpts:
            # Filter out same record in blacklist
            if rcpt_records.get(s) and s not in wl_rcpts:
                rcpt_values.append({'sid': user_id, 'rid': rcpt_records[s], 'wb': 'B'})

    try:
        if values:
            for v in values:
                try:
                    conn.execute("INSERT INTO wblist (sid, rid, wb) VALUES ({}, {}, {})".format(sqlquote(v['sid']),
                                                                                                sqlquote(v['rid']),
                                                                                                sqlquote(v['wb'])))
                except Exception as e:
                    logger.error(e)

        if rcpt_values:
            for v in rcpt_values:
                try:
                    conn.execute("INSERT INTO outbound_wblist (sid, rid, wb) VALUES ({}, {}, {})".format(sqlquote(v['sid']),
                                                                                                         sqlquote(v['rid']),
                                                                                                         sqlquote(v['wb'])))
                except Exception as e:
                    logger.error(e)

    except Exception as e:
        return (False, e)

    return (True, )
Example #11
0
def delete_wblist(conn,
                  account,
                  wl_senders=None,
                  bl_senders=None,
                  wl_rcpts=None,
                  bl_rcpts=None):
    if not utils.is_valid_amavisd_address(account):
        return (False, 'INVALID_ACCOUNT')

    # Remove duplicate.
    if wl_senders:
        wl_senders = list({str(s).lower()
                           for s in wl_senders
                           if utils.is_valid_amavisd_address(s)})

    # Whitelist has higher priority, don't include whitelisted sender.
    if bl_senders:
        bl_senders = list({str(s).lower()
                           for s in bl_senders
                           if utils.is_valid_amavisd_address(s)})

    if wl_rcpts:
        wl_rcpts = list({str(s).lower()
                         for s in wl_rcpts
                         if utils.is_valid_amavisd_address(s)})

    if bl_rcpts:
        bl_rcpts = list({str(s).lower()
                         for s in bl_rcpts
                         if utils.is_valid_amavisd_address(s)})

    # Get account id from `amavisd.users`
    qr = get_user_record(conn=conn, account=account)

    if qr[0]:
        user_id = qr[1]['id']
    else:
        return qr

    # Remove wblist.
    # No need to remove unused senders in `mailaddr` table, because we
    # have daily cron job to delete them (tools/cleanup_amavisd_db.py).
    wl_smails = []
    wl_rmails = []
    bl_smails = []
    bl_rmails = []
    try:
        # Get `mailaddr.id` for wblist senders
        if wl_senders:
            sids = []

            sql = "SELECT id, email FROM mailaddr WHERE email in %s" % sqlquote(wl_senders)
            qr = conn.execute(sql)
            sql_records = qr.fetchall()

            for r in sql_records:
                (_id, _email) = r
                sids.append(int(_id))
                wl_smails.append(utils.bytes2str(_email))

            if sids:
                conn.execute("DELETE FROM wblist WHERE rid={} AND sid IN {} AND wb='W'".format(sqlquote(user_id), sqlquote(sids)))

        if bl_senders:
            sids = []

            sql = "SELECT id, email FROM mailaddr WHERE email IN %s" % sqlquote(bl_senders)
            qr = conn.execute(sql)
            sql_records = qr.fetchall()

            for r in sql_records:
                (_id, _email) = r
                sids.append(int(_id))
                bl_smails.append(utils.bytes2str(_email))

            if sids:
                conn.execute("DELETE FROM wblist WHERE rid={} AND sid IN {} AND wb='B'".format(sqlquote(user_id), sqlquote(sids)))

        if wl_rcpts:
            rids = []

            sql = "SELECT id, email FROM mailaddr WHERE email IN %s" % sqlquote(wl_rcpts)
            qr = conn.execute(sql)
            sql_records = qr.fetchall()

            for r in sql_records:
                (_id, _email) = r
                rids.append(int(_id))
                wl_rmails.append(utils.bytes2str(_email))

            if rids:
                conn.execute("DELETE FROM outbound_wblist WHERE sid={} AND rid IN {} AND wb='W'".format(sqlquote(user_id), sqlquote(rids)))

        if bl_rcpts:
            rids = []

            sql = "SELECT id, email FROM mailaddr WHERE email IN %s" % sqlquote(bl_rcpts)
            qr = conn.execute(sql)
            sql_records = qr.fetchall()

            for r in sql_records:
                (_id, _email) = r
                rids.append(int(_id))
                bl_rmails.append(utils.bytes2str(_email))

            if rids:
                conn.execute("DELETE FROM outbound_wblist WHERE sid={} AND rid IN {} AND wb='B'".format(sqlquote(user_id), sqlquote(rids)))

    except Exception as e:
        return (False, str(e))

    return (True, {'wl_senders': wl_smails,
                   'wl_rcpts': wl_rmails,
                   'bl_senders': bl_smails,
                   'bl_rcpts': bl_rmails})
def restriction(**kwargs):
    sasl_username = kwargs['sasl_username']
    recipient = kwargs['recipient_without_ext']
    recipient_ldif = kwargs['recipient_ldif']

    if sasl_username == recipient:
        return SMTP_ACTIONS[
            'default'] + ' (sasl_username == recipient, not a mail list account)'

    # Pass if recipient doesn't exist (no LDIF data)
    if not recipient_ldif:
        return SMTP_ACTIONS[
            'default'] + ' (Recipient is not a local account - no LDIF data)'

    # Pass if recipient is not a mailing list account
    if 'mailList' not in recipient_ldif['objectClass']:
        return SMTP_ACTIONS[
            'default'] + ' (Recipient is not a mailing list account)'

    # Reject if mailing list is disabled.
    # NOTE: Postfix doesn't query account status of mailing list, so we need
    #       to do it here.
    if recipient_ldif.get('accountStatus', []) != ['active']:
        logger.debug('Recipient (mailing list) is disabled, message rejected.')
        return SMTP_ACTIONS['reject']

    # Get access policy
    policy = recipient_ldif.get('accessPolicy',
                                [MAILLIST_POLICY_PUBLIC])[0].lower()

    # Log access policy
    logger.debug('Access policy of mailing list ({}): {}'.format(
        recipient, policy))

    if policy == MAILLIST_POLICY_PUBLIC:
        return SMTP_ACTIONS[
            'default'] + ' (Access policy: %s, no restriction)' % MAILLIST_POLICY_PUBLIC
    elif policy == 'allowedonly':
        # 'allowedonly' is policy name used by old iRedAPD releases.
        policy = MAILLIST_POLICY_MODERATORS

    if 'mlmmj' in recipient_ldif.get('enabledService', []):
        if policy in [MAILLIST_POLICY_MEMBERSONLY, MAILLIST_POLICY_MODERATORS]:
            logger.debug(
                'Recipient is a mlmmj mailing list, let mlmmj handle the ACL.')
            return SMTP_ACTIONS['default']

    conn = kwargs['conn_vmail']
    sender = kwargs['sender_without_ext']
    sender_domain = kwargs['sender_domain']
    recipient_domain = kwargs['recipient_domain']

    # Get primary recipient domain and all its alias domains
    valid_rcpt_domains = conn_utils.get_primary_and_alias_domains(
        conn=conn, domain=recipient_domain)
    logger.debug(
        'Primary and all alias domain names of recipient domain ({}): {}'.
        format(recipient_domain, ', '.join(valid_rcpt_domains)))

    #
    # No matter what access policy it has, bypass explictly allowed senders
    #
    explicitly_allowed_senders = recipient_ldif.get('listAllowedUser', [])

    # Check sender and sender domains
    if sender in explicitly_allowed_senders:
        return SMTP_ACTIONS[
            'default'] + '  (Sender is allowed explicitly: %s)' % sender
    elif sender_domain in explicitly_allowed_senders or '*@' + sender_domain in explicitly_allowed_senders:
        return SMTP_ACTIONS[
            'default'] + '  (Sender domain is allowed explicitly: %s)' % sender_domain

    # Check all possible sender domains (without checking sender alias domains)
    _possible_sender_domains = [sender_domain]
    _domain_parts = sender_domain.split('.')
    for _ in _domain_parts:
        _possible_sender_domains += ['.' + '.'.join(_domain_parts)]
        _domain_parts.pop(0)

    logger.debug('Sender domain and sub-domains: %s' %
                 ', '.join(_possible_sender_domains))
    if set(_possible_sender_domains) & set(explicitly_allowed_senders):
        return SMTP_ACTIONS[
            'default'] + ' (Sender domain or its sub-domain is explicitly allowed)'

    logger.debug(
        'Sender is not explicitly allowed, perform extra LDAP query to check access.'
    )

    # Get domain dn.
    dn_rcpt_domain = 'domainName=' + recipient_domain + ',' + settings.ldap_basedn

    # Verify access policies
    if policy in [MAILLIST_POLICY_DOMAIN, MAILLIST_POLICY_SUBDOMAIN]:
        if policy == MAILLIST_POLICY_DOMAIN:
            # Bypass all users under the same domain.
            if sender_domain in valid_rcpt_domains:
                logger.info(
                    'Sender domain ({}) is allowed by access policy of mailing list: {}.'
                    .format(sender_domain, policy))
                return SMTP_ACTIONS['default']

        elif policy == MAILLIST_POLICY_SUBDOMAIN:
            # Bypass all users under the same domain and all sub domains.
            for d in valid_rcpt_domains:
                if sender_domain == d or sender_domain.endswith('.' + d):
                    logger.info(
                        'Sender domain ({}) is allowed by access policy of mailing list: {}.'
                        .format(d, policy))
                    return SMTP_ACTIONS['default']

        return SMTP_ACTIONS['reject_not_authorized']

    elif policy == MAILLIST_POLICY_MEMBERSONLY:
        # Get all members of mailing list.
        _f = '(&' + \
             '(accountStatus=active)(memberOfGroup=%s)' % (recipient) + \
             '(|(objectclass=mailUser)(objectClass=mailExternalUser))' + \
             ')'

        # Get both mail and shadowAddress.
        search_attrs = ['mail', 'shadowAddress']

        logger.debug('search base dn: %s' % dn_rcpt_domain)
        logger.debug('search scope: SUBTREE')
        logger.debug('search filter: %s' % _f)
        logger.debug('search attributes: %s' % ', '.join(search_attrs))

        qr = conn.search_s(dn_rcpt_domain, 2, _f, search_attrs)

        allowed_senders = []
        for (_dn, _ldif) in qr:
            _ldif = utils.bytes2str(_ldif)
            for k in search_attrs:
                allowed_senders += _ldif.get(k, [])

        if sender in allowed_senders:
            logger.info(
                'Sender ({}) is allowed by access policy of mailing list: {}.'.
                format(sender, policy))
            return SMTP_ACTIONS['default']

        return SMTP_ACTIONS['reject_not_authorized']

    elif policy == MAILLIST_POLICY_MEMBERSANDMODERATORSONLY:
        # Get both members and moderators.
        _f = '(|' + \
             '(&(memberOfGroup=%s)(|(objectClass=mailUser)(objectClass=mailExternalUser)))' % recipient + \
             '(&(objectclass=mailList)(mail=%s))' % recipient + \
             ')'
        search_attrs = ['mail', 'shadowAddress', 'listAllowedUser']

        logger.debug('search base dn: %s' % dn_rcpt_domain)
        logger.debug('search scope: SUBTREE')
        logger.debug('search filter: %s' % _f)
        logger.debug('search attributes: %s' % ', '.join(search_attrs))

        allowed_senders = []
        try:
            qr = conn.search_s(dn_rcpt_domain, 2, _f, search_attrs)
            logger.debug('search result: %s' % repr(qr))

            # Collect values of all search attributes
            for (_dn, _ldif) in qr:
                _ldif = utils.bytes2str(_ldif)
                for k in search_attrs:
                    allowed_senders += _ldif.get(k, [])

            if sender in allowed_senders:
                logger.info(
                    'Sender ({}) is allowed by access policy of mailing list: {}.'
                    .format(sender, policy))
                return SMTP_ACTIONS['default']
        except Exception as e:
            _msg = 'Error while querying allowed senders of mailing list (access policy: {}): {}'.format(
                MAILLIST_POLICY_MEMBERSANDMODERATORSONLY, repr(e))
            logger.error(_msg)
            return SMTP_ACTIONS['default'] + ' (%s)' % _msg

        return SMTP_ACTIONS['reject_not_authorized']

    elif policy == MAILLIST_POLICY_MODERATORS:
        # If sender is hosted on local server, check per-user alias addresses
        # and alias domains.

        # Already checked `listAllowedUser` above before checking any access
        # policy, so it's safe to reject here..
        if not kwargs['sasl_username']:
            return SMTP_ACTIONS['reject_not_authorized']

        # Remove '*@domain.com'
        allowed_senders = [
            s for s in explicitly_allowed_senders if not s.startswith('*@')
        ]

        # Separate email addresses and domain names
        _users = []
        _domains = []

        for _as in allowed_senders:
            if utils.is_email(_as):
                if _as.endswith('@' + recipient_domain):
                    _users.append(_as)

                    # We will add both `_as` and its shadowAddress back later.
                    allowed_senders.remove(_as)
            else:
                if _as.startswith('.'):
                    _domains.append(_as.lstrip('.'))
                else:
                    _domains.append(_as)

                # We will add both `_as` and its alias domains back later.
                allowed_senders.remove(_as)

        logger.debug('Allowed users: %s' % ', '.join(_users))
        logger.debug('Allowed domains: %s' % ', '.join(_domains))

        # Get per-user alias addresses.
        if _users:
            logger.debug(
                "[+] Getting per-account alias addresses of allowed senders.")

            _basedn = 'ou=Users,' + dn_rcpt_domain
            _f = '(&(objectClass=mailUser)(enabledService=shadowaddress)(|'
            for i in _users:
                _f += '(mail={})(shadowAddress={})'.format(i, i)
            _f += '))'

            _search_attrs = ['mail', 'shadowAddress']

            logger.debug('base dn: %s' % _basedn)
            logger.debug('search scope: ONELEVEL')
            logger.debug('search filter: %s' % _f)
            logger.debug('search attributes: %s' % ', '.join(_search_attrs))

            qr = conn.search_s(_basedn, 1, _f, _search_attrs)
            logger.debug('query result: %s' % str(qr))

            for (_dn, _ldif) in qr:
                _ldif = utils.bytes2str(_ldif)
                for k in _search_attrs:
                    allowed_senders += _ldif.get(k, [])

        if _domains:
            logger.debug(
                '[+] Getting alias domains of allowed sender (sub-)domains.')

            _basedn = settings.ldap_basedn
            _f = '(&(objectClass=mailDomain)(enabledService=domainalias)(|'
            for i in _domains:
                _f += '(domainName={})(domainAliasName={})'.format(i, i)
            _f += '))'

            _search_attrs = ['domainName', 'domainAliasName']

            logger.debug('base dn: %s' % _basedn)
            logger.debug('search scope: ONELEVEL')
            logger.debug('search filter: %s' % _f)
            logger.debug('search attributes: %s' % ', '.join(_search_attrs))

            qr = conn.search_s(_basedn, 1, _f, _search_attrs)
            logger.debug('result: %s' % str(qr))

            for (_dn, _ldif) in qr:
                _all_domains = []
                for k in _search_attrs:
                    _all_domains += _ldif.get(k, [])

                for domain in _all_domains:
                    if domain in _domains:
                        # Add original domain and alias domains
                        allowed_senders += [d for d in _all_domains]

        if sender in allowed_senders or sender_domain in allowed_senders:
            logger.info(
                'Sender ({}) is allowed by access policy of mailing list: {}.'.
                format(sender, policy))
            return SMTP_ACTIONS['default']
        else:
            return SMTP_ACTIONS['reject_not_authorized']

    return SMTP_ACTIONS[
        'default'] + ' (Unknown access policy: %s, no restriction)' % policy