auth_ds – Datasource based SMTP Authentication

March 26, 2020 Contributors

**Configuration Change. ** This module is loaded automatically as required and does not need to be explicitly included.

With this module Momentum can provide authenticated SMTP sessions via SMTP AUTH by using any supported datasource as the authentication bridge. When a user connects to Momentum, if authentication is supported, it will attempt to authenticate the user by issuing a query against the configured datasource. If the result of that query is "true" then the user was successfully identified. Since the module uses the datasource layer it can take advantage of the built-in caching mechanism to avoid putting pressure on your authentication data stores. The ds_core module and datasource drivers are loaded automatically as required and do not need to be explicitly included. See “ds_core – Datasource Query Core” for more information.

The webui-common.conf file has a working auth_ds configuration that is enabled by default.

You may define multiple authentication schemes against different datasources, and configure your listeners to use a different scheme depending on the connecting IP address.

Note

Any hosts specified by the relay_hosts option are not subject to any SMTP authentication that you may have implemented. Likewise when open_relay is set to true. For more information see relay_hosts.

When a user attempts to authenticate, the SMTP listener routes the authentication attempt to the authentication module, which then uses the query that you’ve configured. If the results of the query are not already known, a query is run asynchronously to obtain them, and the SMTP session is suspended, allowing Momentum to continue to service other sessions. When the results come back, the first column of the first row is inspected; if it is a non-zero value, then the authentication is considered successful.

If you’re using a datasource, such as LDAP, where it is not possible to guarantee the ordering of the results returned, you may add a password_column option to the auth_ds module configuration to specify which named column holds the password in the result set.

auth_ds {
  Scheme "ecauth" {
    Authenticate {
      ...
      # this scheme returns a password in the column named "password"
      password_column = "password"
      returns_password = "true"
    }
  }
}

auth_ds {
  Scheme "auth" {
    Authenticate {
      query = "SELECT password FROM webconsole.users WHERE username = :user"
      cache = "auth"
      map = [
        user = "%{username}"
      ]
      type = "md5"
      returns_password = "true"
      password_column = "password"
    }
    QueryGroups {
      query = "SELECT groupname FROM webconsole.groupassignments WHERE username = :user"
      cache = "auth"
      group_column = "groupname"
      map = [
        user = "%{username}"
      ]
    }
    EnumGroups {
      query = "SELECT role from webconsole.roles"
      cache = "auth"
      group_column = "role"
    }
  }
}

Configuration Options

The following options are available in authentication schemes:

cache

The datasource cache to execute the query against.

EnumGroups

This stanza enumerates all possible groups or roles.

map

The parameter expansion map to use.

query

The query to be executed.

QueryGroups

All the groups associated with the user.

type

The encryption type for passwords in the data store. Enabled this by setting the type parameter within the ecauth block. If this is unspecified you can extract this from the data store as well using the type_column option.

The type parameter can take the values "ucrypt" and "md5" specifying Unix style and digest md5 style respectively.

type_column

The column name containing the encryption type for a given password. This corresponds with a type_map specifier.

type_map

A dictionary that maps a column to an encryption type. See The type_map Option for complete information.

returns_password

Whether or not the query returns a password for validation inside the module, or simply a truth value (for validation inside the DB).

**EnumGroups and QueryGroups. ** Typically the EnumGroups and QueryGroups stanzas are used with the built-in webconsole tables. They can also be used to plug in to an external authentication system defined in an Authorization stanza. For example, the EnumGroup stanza allows you to interrogate the groups and then present those in a list for assigning permissions via an Authorization stanza. See also authorization.

**The type_map Option. ** Type maps contain key-value pairs where the key is the contents of the column identified by the type_column option, and the value is one of the following:

clear

Cleartext

ucrypt

Unix crypt

sha1_hex

SHA1 encryption (hex representation)

sha1_base64

SHA1 encryption (base64 representation)

ssha1

Seeded SHA1 encryption

md5

MD5 encryption

md5_hex

MD5 encryption (hex representation)

md5_base64

MD5 encryption (base64 representation)

hmac_md5

HMAC MD5 encryption

digest-md5

Digest-MD5 encryption

smd5

Seeded MD5 encryption

rfc2307

RFC 2307 encryption specifiers (crypt, md5, smd5, sha, ssha)

Parameter expansion maps

Use parameter expansion maps to map authentication parameters into your query. The maps consist of parameter names that map to interpolated value strings based on the authentication parameters listed below. This approach allows Momentum to take advantage of native prepared statement support that may be present in the underlying datasource drivers, while still allowing you a certain amount of freedom for string building.

The right hand side of the expansion maps consists of a string value. The string can contain %{parametername}, where parametername corresponds to one of the parameters listed below. When the query is executed, the parameter name is expanded to its value before being passed on to the data source. You may interpolate multiple parameters in a single string in this way, if desired.

crypt

Describes the level of crypto in use. Can be one of clear (for LOGIN), md5, hmac_md5 (for CRAM-MD5) or digest-md5 (for DIGEST-MD5).

username

The username.

uri

The uri specified in the listener configuration.

password

The password. This may be cleartext or it may have some level of crypto applied to it, according to the crypt parameter.

nonce

The nonce for MD5 based authenticators.

realm , digest-uri

Parameters for DIGEST-MD5 based auth.

Authenticating against MySQL

The following configuration excerpt demonstrates how to configure Momentum to authenticate against MySQL:

# Define a cache named authdb
# that talks to a mysql server
Datasource "authdb" {
  uri = ( "mysql:host=localhost;port=3306;dbname=ecauth;user=root;password=password" )
}

# Load the authentication module
auth_ds {
  # register "mysqlauth" as an authentication scheme
  # this is referenced by the Esmtp_Listener below.
  Scheme "mysqlauth" {
    Authenticate {
      # This query will return a row containing the value 1 if
      # the username and password match up
        query = "SELECT 1 AS ALLOW FROM users WHERE user_name = :user and
          password = encrypt(:pass, PASSWORD)"
      # The query will be presented against the MySQL cache
      cache = "authdb"
      # This map is used to resolve the placeholders in the query by substituting
      # the username and password supplied by the user.

      map = [
        user = "%{username}"
        pass = "%{password}"
      ]
    }
  }
}

ESMTP_Listener {
  Listen ":25" {
    SMTP_Extensions = (
      "ENHANCEDSTATUSCODES"
      "STARTTLS"
      "AUTH LOGIN"
    )
    AuthLoginParameters = [uri = "mysqlauth://"]
    SMTP_Extensions = ( "ENHANCEDSTATUSCODES" "AUTH LOGIN" )
  }
}

Note

For licensing reasons the MySQL module does not ship with Momentum and must be downloaded separately. For instructions on downloading and installing this module see “MySQL”.

Authentication against LDAP

The precise configuration used to implement LDAP based authentication varies depending on your LDAP schema. If you are migrating from our older LDAP auth module, the authentication concept was that the username and password from the SMTP session would be used as the bind name and password used to establish a connection to the LDAP server. If the bind attempt was successful, then the user was deemed to have been authenticated. With this module, we also need to obtain results from an LDAP query.

The suggested approach for implementing LDAP authentication using the datasource layer is to craft an LDAP query that returns the common name of the user you are authenticating.

In the case of an LDAP directory like the Openwave directory, you will need to be able to specify a type_map so that the server can correctly encrypt the provided password for comparison. The following example shows integration against an openwave directory:

auth_ds {
  Scheme "myscheme_openwave" {
    Authenticate {
      query = "ldap:///?mailpassword,mailpasswordtype?sub? »
              (|(maillogin=$user)(mail=$user)(mailalternateaddress=$user))"
      cache = "openwave_directory"
      returns_password = "true"
      password_column = "mailpassword"
      type_column = "mailpasswordtype"
      map = [
        user = "%{username}"
      ]
      type_map = [
        C = "clear"
        U = "ucrypt"
        H = "sha1_hex"
        S = "ssha1"
        M = "smd5"
      ]
    }
  }
}

The LDAP driver supports a bind-only mode that allows connection and authentication without execution of a query. If it succeeds, it returns a "bind" column with a value of 1. This is useful in cases where it is desirable to pass the credentials through from SMTP authentication to the LDAP directory, for example, to eliminate the need to change the LDAP query if authentication settings change.

The following example shows how to use the bind only feature.

auth_ds {
  Scheme "ldapauthscheme" {
    Authenticate {
      query = "ldap://ldaphostname/????bindname=uid=$user%2Cou=people »
              %2Cdc=example%2Cdc=com,x-bindpw=$pass"
      cache = "ldapauth"
      map =  [
        user = "%{username}"
        pass = "%{password}"
      ]
    }
  }
}