How-to: Configure a Samba check password script in UCS

A Samba check password script can be used to add custom password complexity rules for password changes processed by a Samba AD DC.

The script receives the new password on standard input and returns 0 if the password is accepted or a non-zero value if it is rejected.

Prerequisites and scope

The Samba domain setting “Passwords must meet complexity requirements” must be enabled. You can verify this on a Samba AD DC with:

samba-tool domain passwordsettings show

The output must contain:

Password complexity: on

The check password script is executed only for password changes processed by Samba. UDM-based password resets, such as the End User Self Service “Forgot password” workflow, do not use this script.

The script runs as root. It therefore must be owned by root and must not be writable by regular users.

Samba provides information about the affected user through environment variables:

  • SAMBA_CPS_ACCOUNT_NAME: the user’s sAMAccountName
  • SAMBA_CPS_FULL_NAME: the user’s displayName, if available
  • SAMBA_CPS_USER_PRINCIPAL_NAME: the user’s userPrincipalName, if available

Use these environment variables instead of passing a username with %u.

A configured check password script replaces Samba’s built-in character-category complexity check. If the standard Samba complexity requirements should still apply, the custom script must perform this check itself.

Other Samba domain password settings, such as the minimum password length, are handled separately.

Example script

The following example:

  • preserves Samba’s standard character-category complexity check;
  • rejects passwords containing the account name;
  • rejects passwords containing significant parts of the user’s full name;
  • rejects passwords containing significant parts of the UCS Kerberos realm.

Create /usr/local/sbin/univention-samba-check-password with the following content:

#!/usr/bin/python3

import os
import re
import sys

from samba import check_password_quality
from univention.config_registry import ConfigRegistry


MIN_NAME_PART_LENGTH = 4
MIN_REALM_PART_LENGTH = 3


def reject(message):
    print(message, file=sys.stderr)
    return 1


def main():
    password = sys.stdin.read().rstrip('\n')
    password_lower = password.lower()

    account_name = os.environ.get('SAMBA_CPS_ACCOUNT_NAME')
    full_name = os.environ.get('SAMBA_CPS_FULL_NAME')

    ucr = ConfigRegistry()
    ucr.load()
    kerberos_realm = ucr.get('kerberos/realm', '')

    # Preserve Samba's standard character-category complexity check.
    if not check_password_quality(password):
        return reject('Password does not meet complexity requirements.')

    if (
        account_name
        and len(account_name) >= MIN_NAME_PART_LENGTH
        and account_name.lower() in password_lower
    ):
        return reject('Password contains user account name.')

    if full_name:
        for namepart in re.split(r'[-,._# \t]+', full_name):
            if (
                len(namepart) >= MIN_NAME_PART_LENGTH
                and namepart.lower() in password_lower
            ):
                return reject('Password contains parts of the full user name.')

    if kerberos_realm:
        for realm_part in re.split(r'[.-]+', kerberos_realm):
            if (
                len(realm_part) >= MIN_REALM_PART_LENGTH
                and realm_part.lower() in password_lower
            ):
                return reject('Password contains parts of the Kerberos realm.')

    return 0


if __name__ == '__main__':
    sys.exit(main())

MIN_NAME_PART_LENGTH and MIN_REALM_PART_LENGTH can be adjusted to the local requirements. Avoid very small values unless this is intentional, as short strings may occur in many otherwise unrelated passwords.

For example, with the realm SCHOOL.DISTRICT-EXAMPLE.DE, the default above rejects SCHOOL, DISTRICT, and EXAMPLE, but does not reject DE on its own.

Install and configure the script

Set the ownership and permissions:

chown root:root /usr/local/sbin/univention-samba-check-password
chmod 0755 /usr/local/sbin/univention-samba-check-password

Configure Samba through UCR:

ucr set 'samba/global/options/check password script=/usr/local/sbin/univention-samba-check-password'

Restart Samba afterwards:

/etc/init.d/samba restart

Multiple Samba AD DCs

The setting and the script are local to each Samba AD DC.

If password changes can be processed by multiple Samba AD DCs, install the same script and set the UCR variable on every Samba AD DC.

Configuring the script only on the server specified in kerberos/kpasswdserver covers Kerberos password changes directed to that server, but other password changes, for example from Windows domain clients, may be processed by another Samba AD DC.

Restart the Samba AD DCs one at a time when applying the configuration in a production environment.

Verification

Verify the generated Samba configuration:

testparm -s | grep 'check password script'

The result should contain:

check password script = /usr/local/sbin/univention-samba-check-password

You can then test a user password change, for example with:

kpasswd <USERNAME>

A password rejected by the script causes the password change to fail.

The exact error message printed by the script is not necessarily passed through to the client. UMC, Kerberos, or Windows may display only a generic password complexity or password restriction message.

Removing the custom check

Remove the UCR setting and restart Samba:

ucr unset 'samba/global/options/check password script'
/etc/init.d/samba restart