Commits

Zhang Huangbin  committed 1dec67d

* Fixed: Incorrect detection of sender domain (missing '@') in plugin sql_user_restrictions.py. Thanks Tim Lau for the report.
* New plugin to force user to change password in certain days. Please read comment in plugin file to understand how to configure it and how it works.
+ For LDAP backend: ldap_force_change_password_in_days
+ For MySQL/PostgreSQL backend: sql_force_change_password_in_days

  • Participants
  • Parent commits bee45bc

Comments (0)

Files changed (4)

+iRedAPD-1.4.3:
+    * New plugin to force user to change password in certain days. Please
+      read comment in plugin file to understand how to configure it and how
+      it works.
+        + For LDAP backend: ldap_force_change_password_in_days
+        + For MySQL/PostgreSQL backend: sql_force_change_password_in_days
+
+    * Fixed:
+        + Incorrect detection of sender domain (missing '@') in plugin
+          sql_user_restrictions.py.
+          Thanks Tim Lau for the report.
+
 iRedAPD-1.4.2:
     * Fixed:
         + Incorrect detection of domain (missing '@') in plugin

File plugins/ldap_force_change_password_in_days.py

+# Author: Zhang Huangbin <zhb _at_ iredmail.org>
+# Purpose: Check date of user password last change and reject smtp session if
+#          user didn't change password in 90 days.
+#
+# Below settings can be placed in iRedAPD config file 'settings.py':
+#
+#   - CHANGE_PASSWORD_DAYS: value must be an integer number. e.g. 90 (90 days)
+#   - CHANGE_PASSWORD_MESSAGE: message string which will be read by user
+#
+# Sample settings:
+#
+#   - CHANGE_PASSWORD_DAYS = 90
+#   - CHANGE_PASSWORD_MESSAGE = 'Please change your password in webmail immediately: https://xxx/webmail/'
+#
+# How it works:
+#
+#   - iRedMail configures plugin 'password' of Roundcube webmail to store
+#     password change date in SQL database `vmail`, column
+#     `mailbox.passwordlastchange`.
+#
+#   - This plugin checks date stored in `mailbox.passwordlastchange` and
+#     compare it with current date.
+
+import datetime
+import logging
+import settings
+from libs import SMTP_ACTIONS
+
+REQUIRE_LOCAL_SENDER = True
+REQUIRE_LOCAL_RECIPIENT = False
+SENDER_SEARCH_ATTRLIST = ['shadowLastChange']
+RECIPIENT_SEARCH_ATTRLIST = []
+
+# You can override below two settings in iRedAPD config file 'settings.py'.
+# Force to change password in 90 days.
+try:
+    CHANGE_PASSWORD_DAYS = settings.CHANGE_PASSWORD_DAYS
+except:
+    CHANGE_PASSWORD_DAYS = 90
+
+# Reject reason.
+# It's recommended to add URL of your webmail in this message.
+try:
+    CHANGE_PASSWORD_MESSAGE = settings.CHANGE_PASSWORD_MESSAGE
+except:
+    CHANGE_PASSWORD_MESSAGE = 'Please change your password in webmail before sending email'
+
+reject_action = 'REJECT ' + CHANGE_PASSWORD_MESSAGE
+
+
+def get_days_of_today():
+    """Return number of days since 1970-01-01."""
+    today = datetime.date.today()
+
+    try:
+        return (datetime.date(today.year, today.month, today.day) - datetime.date(1970, 1, 1)).days
+    except:
+        return 0
+
+
+def restriction(**kwargs):
+    if not kwargs['sasl_username']:
+        logging.debug('DUNNO Not an authenticated user (no sasl_username in smtp session)')
+        return 'DUNNO Not an authenticated user (no sasl_username in smtp session)'
+
+    if not kwargs['sender_ldif']:
+        logging.debug('DUNNO Not a local user (no sender ldif)')
+        return 'DUNNO Not a local user (no sender ldif)'
+
+    sender_ldif = kwargs['sender_ldif']
+
+    # Get (an integer) value of attribute 'shadowLastChange'.
+    shadow_last_change = int(sender_ldif.get('shadowLastChange', [0])[0])
+    days_of_today = get_days_of_today()
+
+    # Days since password last change
+    passed_days = days_of_today - shadow_last_change
+
+    logging.debug('Days of password last change: %d (today: %d)' % (shadow_last_change, days_of_today))
+
+    if passed_days >= CHANGE_PASSWORD_DAYS:
+        logging.debug("Password last change date is older than %d days." % CHANGE_PASSWORD_DAYS)
+        return reject_action
+
+    logging.debug("Sender will be forced to change password in %d day(s)." % (CHANGE_PASSWORD_DAYS - passed_days))
+    return SMTP_ACTIONS['default']

File plugins/sql_force_change_password_in_days.py

+# Author: Zhang Huangbin <zhb _at_ iredmail.org>
+# Purpose: Check date of user password last change and reject smtp session if
+#          user didn't change password in 90 days.
+#
+# Below settings can be placed in iRedAPD config file 'settings.py':
+#
+#   - CHANGE_PASSWORD_DAYS: value must be an integer number. e.g. 90 (90 days)
+#   - CHANGE_PASSWORD_MESSAGE: message string which will be read by user
+#
+# Sample settings:
+#
+#   - CHANGE_PASSWORD_DAYS = 90
+#   - CHANGE_PASSWORD_MESSAGE = 'Please change your password in webmail immediately: https://xxx/webmail/'
+#
+# How it works:
+#
+#   - iRedMail configures plugin 'password' of Roundcube webmail to store
+#     password change date in SQL database `vmail`, column
+#     `mailbox.passwordlastchange`.
+#
+#   - This plugin checks date stored in `mailbox.passwordlastchange` and
+#     compare it with current date.
+
+import datetime
+import logging
+import settings
+from libs import SMTP_ACTIONS
+
+# You can override below two settings in iRedAPD config file 'settings.py'.
+# Force to change password in 90 days.
+try:
+    CHANGE_PASSWORD_DAYS = int(settings.CHANGE_PASSWORD_DAYS)
+except:
+    CHANGE_PASSWORD_DAYS = 90
+
+# Reject reason.
+# It's recommended to add URL of your webmail in this message.
+try:
+    CHANGE_PASSWORD_MESSAGE = settings.CHANGE_PASSWORD_MESSAGE
+except:
+    CHANGE_PASSWORD_MESSAGE = 'Please change your password in webmail before sending email'
+
+reject_action = 'REJECT ' + CHANGE_PASSWORD_MESSAGE
+
+def restriction(**kwargs):
+    if not kwargs['sasl_username']:
+        return 'DUNNO Not a local user'
+
+    # Get `mailbox.passwordlastchange`.
+    sasl_username = kwargs['sasl_username']
+    sql = """SELECT passwordlastchange FROM mailbox WHERE username='%s' LIMIT 1""" % sasl_username
+    logging.debug('SQL to get mailbox.passwordlastchange of sender (%s): %s' % (sasl_username, sql))
+
+    conn = kwargs['conn']
+    conn.execute(sql)
+    sql_record = conn.fetchone()
+    logging.debug('Returned SQL Record: %s' % str(sql_record))
+
+    if sql_record:
+        pwchdate = sql_record[0]
+        logging.debug('Date of password last change: %s' % str(pwchdate))
+    else:
+        logging.debug('No SQL record of this user.')
+        return SMTP_ACTIONS['default']
+
+    # Compare date to make sure it's less than CHANGE_PASSWORD_DAYS.
+    shift = datetime.datetime.now() - pwchdate
+    if not shift < datetime.timedelta(days=CHANGE_PASSWORD_DAYS):
+        logging.debug("Password last change date is older than %d days." % CHANGE_PASSWORD_DAYS)
+        return reject_action
+    else:
+        logging.debug("Sender didn't change password before.")
+        return reject_action
+
+    logging.debug("Sender will be forced to change password on %s." % str(pwchdate + datetime.timedelta(days=CHANGE_PASSWORD_DAYS)))
+    return SMTP_ACTIONS['default']

File plugins/sql_user_restrictions.py

 
             if all_allowed_senders:
                 if sender in all_allowed_senders \
-                   or sender_domain in all_allowed_senders \
+                   or '@' + sender_domain in all_allowed_senders \
                    or '@.' + sender_domain in all_allowed_senders \
                    or '@.' in all_allowed_senders:
                     return SMTP_ACTIONS['accept']
 
             if all_rejected_senders:
                 if sender in all_rejected_senders \
-                   or sender_domain in all_rejected_senders \
+                   or '@' + sender_domain in all_rejected_senders \
                    or '@.' + sender_domain in all_rejected_senders \
                    or '@.' in all_rejected_senders:
                     return SMTP_ACTIONS['reject']