mod_sql_passwd
Many FTP sites use SQL databases for storing user accounts, including the user name and password. And while the mod_sql module provides support for some formats for the passwords stored in SQL databases, many sites have other formats which are not supported by mod_sql. These other formats often include MD5 or SHA1 passwords, base64-encoded or hex-encoded, without the prefix which is required by mod_sql's "OpenSSL" SQLAuthType.
mod_sql
SQLAuthType
The mod_sql_passwd module provides support for some of these other formats. When the mod_sql_passwd module is enabled, you can configure SQLAuthTypes of:
SQLAuthTypes
This module is contained in the mod_sql_passwd.c file for ProFTPD 1.3.x, and is not compiled by default. Installation instructions are discussed here; a discussion on usage is also available.
mod_sql_passwd.c
This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/).
This product includes cryptographic software written by Eric Young (eay@cryptsoft.com).
The most current version of mod_sql_passwd is distributed with ProFTPD.
Please contact TJ Saunders <tj at castaglia.org> with any questions, concerns, or suggestions regarding this module.
<VirtualHost>
<Global>
The SQLPasswordArgon2 directive configures the length of the calculated Argon2 output hash in bytes. The default length is 32 bytes.
SQLPasswordArgon2
The SQLPasswordCost directive configures the high-level cost settings to use for memory-hard algorithms like scrypt and argon2. The supported cost cost values are:
SQLPasswordCost
scrypt
argon2
This cost uses parameters where generating the value is part of an "interactive" session.
For scrypt, depending on the version of libsodium used, this cost uses:
libsodium
N=16384, r=8, p=1
This cost uses parameters where the value generated is considered very "sensitive".
N=1048576, r=8, p=1
The SQLPasswordEncoding directive configures the encoding that mod_sql_passwd expects when handling password values retrieved from a SQL database.
SQLPasswordEncoding
The following encoding values are currently supported:
If no SQLPasswordEncoding directive is configured, mod_sql_passwd will use "hex" by default.
The SQLPasswordEngine directive enables or disables the module's registered SQLAuthType handlers.
SQLPasswordEngine
The SQLPasswordOptions directive is used to configure various behaviors of mod_sql_passwd. Note: all of the configured SQLPasswordOptions parameters must appear on the same line in the configuration; only the first SQLPassworOptions directive that appears in the configuration is used.
SQLPasswordOptions
SQLPassworOptions
Example:
SQLPasswordOptions HashEncodeSalt HashEncodePassword
The following options are currently supported:
HashPassword
HashEncodePassword
HashSalt
HashEncodeSalt
See the transformations section for a fuller description of how mod_sql_passwd operates on the password and salt data.
The SQLPasswordPBKDF2 directive configures the input parameters to be used for PBKDF2 (Password-Based Key Derivation Function, version 2) passwords. The digest parameter specifies the digest algorithm to use (e.g. "sha1" or "sha512"); the iterations specifies the number of iterations to use for the key derivation, and length indicates the number of bytes to emit for the derived key.
SQLPasswordPBKDF2
Note that PBKDF2 requires that a salt be available, e.g. via SQLPasswordSaltFile or SQLPasswordUserSalt.
SQLPasswordSaltFile
SQLPasswordUserSalt
Per RFC 2898, the salt used should be 8 bytes or longer in length; this RFC also recommends that iterations be 1000 or greater.
# Tell mod_sql to use PBKDF2 passwords SQLAuthTypes pbkdf2 ... # Use the SHA1 digest algorithm, 200K iterations, and expect an output # length of 20 bytes. SQLPasswordPBKDF2 sha1 200000 20 SQLPasswordSaltFile /path/to/salt/file
Use of digest algorithms other than SHA1 for SQLPasswordPBKDF2 requires OpenSSL-1.0.0c or later; earlier versions did not have the necessary APIs.
As of proftpd-1.3.5, the SQLPasswordPBKDF2 directive can instead take a named query, for determining the digest algorithm, iteration count, and output length on a per-user basis. For example:
proftpd-1.3.5
SQLNamedQuery get-user-pbkdf2 SELECT "algo, iter, len FROM user_pbkdf2 WHERE user = '%{0}' SQLPasswordPBKDF2 sql:/get-user-pbkdf2
The SQLPasswordRounds directive configures the number of rounds through which the password (and possibly salt) data will be hashed and encoded. The count parameter must be greater than 1.
SQLPasswordRounds
The SQLPasswordSaltEncoding directive configures the encoding that mod_sql_passwd expects when handling salt values retrieved either from a SQL database, or from a file.
SQLPasswordSaltEncoding
If no SQLPasswordSaltEncoding directive is configured, mod_sql_passwd will use "none" by default.
The SQLPasswordSaltFile directive configures a file which contains salt data. This salt will be added to the digest, along with the password sent by the client. Note that the salt will be used for all users.
Since many editors will automatically add a newline when writing a file, the mod_sql_passwd file will automatically trim the last newline in the salt data, if there is one. This means that if your salt must end in a newline character, then your SQLPasswordSaltFile must contain "salt\n\n".
When using salted passwords, some systems will prepend the salt as a prefix to the data, and others will append the salt as a suffix. The optional second parameter to SQLPasswordSaltFile controls how this module will use the salt:
SQLPasswordSaltFile /path/to/salt Prepend
SQLPasswordSaltFile /path/to/salt Append
If no SQLPasswordSaltFile is configured, then no salting is done.
The SQLPasswordScrypt directive configures the length of the calculated Scrypt output hash in bytes. The default length is 32 bytes.
SQLPasswordScrypt
The SQLPasswordUserSalt directive configures a per-user salt that will be added to the digest, along with the password sent by the client.
If "name" is specified, then the per-user salt data will be the name of the user logging in. Alternatively, you can configure a SQLNamedQuery which returns a single column of a single row, containing a string to use as the salt data, e.g.:
SQLNamedQuery
SQLNamedQuery get-user-salt SELECT "salt FROM user_salts WHERE user_name = '%{0}'" SQLPasswordUserSalt sql:/get-user-salt
When using salted passwords, some systems will prepend the salt as a prefix to the data, and others will append the salt as a suffix. The optional second parameter to SQLPasswordUserSalt controls how this module will use the salt:
SQLPasswordUserSalt name Prepend SQLPasswordUserSalt sql:/get-user-salt Prepend
SQLPasswordUserSalt name Append SQLPasswordUserSalt sql:/get-user-salt Append
--enable-openssl
NOTE: it is important that mod_sql_passwd appear after mod_sql in your --with-modules configure option:
--with-modules
$ ./configure --enable-openssl --with-modules=mod_sql:mod_sql_passwd ...
$ ./configure --enable-dso --enable-openssl --with-shared=mod_sql_passwd
$ make $ make install
For those with an existing ProFTPD installation, you can use the prxs tool to add mod_sql_passwd, as a DSO module, to your existing server:
prxs
$ prxs -c -i -d mod_sql_passwd.c
The following examples demonstrate how the mod_sql_passwd can be used.
To configure mod_sql_passwd to handle MD5 passwords that are base64-encoded, use:
<IfModule mod_sql_passwd.c> SQLPasswordEngine on SQLPasswordEncoding base64 </IfModule> <IfModule mod_sql.c> ... # Now that mod_sql_passwd is used, we can configure "MD5" as an # SQLAuthType that mod_sql will handle. SQLAuthTypes MD5 </IfModule>
To have mod_sql_passwd to handle hex-encoded (and in lowercase) passwords, use:
<IfModule mod_sql_passwd.c> SQLPasswordEngine on SQLPasswordEncoding hex </IfModule>
And if for some reason your database values are stored as hex values in uppercase, you would use:
<IfModule mod_sql_passwd.c> SQLPasswordEngine on SQLPasswordEncoding HEX </IfModule>
To use salted passwords, write the salt to use into a file, and configure the mod_sql_passwd module to use it:
<IfModule mod_sql_passwd.c> SQLPasswordEngine on SQLPasswordEncoding hex SQLPasswordSaltFile /path/to/salt </IfModule>
Argon2, Scrypt When mod_sql_passwd is compiled/linked with the libsodium library, then the Argon2 and Scrypt algorithms become available for use:
<IfModule mod_sql_passwd.c> SQLPasswordEngine on SQLPasswordEncoding hex SQLPasswordSaltFile /path/to/salt </IfModule> <IfModule mod_sql.c> ... # Now that mod_sql_passwd is used, we can configure "SCRYPT" as an # SQLAuthType that mod_sql will handle. SQLAuthTypes SCRYPT </IfModule>
The scrypt algorithm requires 32 bytes of salt data; lack of salt, or salt of the wrong amount, will result in authentication failure. The argon2 algorithm requires 16 bytes of salt data; lack of salt or the wrong amount will result in failure.
The argon2 algorithm requires libsodium-1.0.9 or later.
libsodium-1.0.9
Logging The mod_sql_passwd module supports trace logging, via the module-specific log channels:
proftpd.conf
TraceLog /path/to/ftpd/trace.log Trace sql.passwd:20
Processing of Password and Salt Data The logical description of the processing that mod_sql_passwd does can be expressed as: ENCODE(HASH(data)) where data is comprised of the password, and possibly a salt. The function ENCODE() is determined by SQLPasswordEncoding, and the function HASH() by SQLAuthTypes.
ENCODE(HASH(data))
ENCODE()
HASH()
Thus if we use a configuration like:
SQLAuthTypes MD5 SQLPasswordEncoding hex
hex(MD5(data))
Using Salts By default, the mod_sql_passwd module uses the password, as sent by the client, as the data on which to perform its processing. In many cases, however, a salt is needed in addition to the password. The SQLPasswordSaltFile and SQLPasswordUserSalt directives are used to tell mod_sql_passwd that it should add a salt to the data before processing it. These directives also specify whether the salt should be prepended to the password, e.g.:
data = salt + password
data = password + salt
Let's show a configuration which uses a prepended salt:
SQLAuthTypes MD5 SQLPasswordEncoding hex SQLPasswordSaltFile /path/to/salt.data Prepend
hex(MD5(salt + password))
Some sites will have even more complex requirements for how the data processed by mod_sql_passwd need to be constructed. The salt data may need to be hashed before being used with the password, or may need to be hashed and encoded before use. Or maybe the password data needs to be hashed before use with the salt, or hashed and encoded. The SQLPasswordOptions directive supports options for supporting these use cases.
Each of the following examples assumes the following configuration:
This option says that mod_sql_passwd should use the HASH() function on the password data before using it, regardless of whether a salt is used or not. I.e.:
data = salt + HASH(password)
data = salt + MD5(password)
This option says that mod_sql_passwd should use the HASH() function on the salt data before using it. If no salt is used, this option is silently ignored. Thus:
data = HASH(salt) + password
data = MD5(salt) + password
This option says that mod_sql_passwd should use the HASH() function and then the ENCODE() function on the password data before using it. This option is only useful when salts are also used. Thus:
data = salt + ENCODE(HASH(password))
data = salt + hex(MD5(password))
This option says that mod_sql_passwd should use the HASH() function and then the ENCODE() function on the salt data before using it. If no salt is used, this option is silently ignored. Thus:
data = ENCODE(HASH(salt)) + password
data = hex(MD5(salt)) + password
Of course, these various options can be combined:
SQLPasswordOptions HashEncodePassword HashEncodeSalt
data = ENCODE(HASH(salt)) + ENCODE(HASH(password)) data = hex(MD5(salt)) + hex(MD5(password))
Rounds For convenience, let's assume that the function TRANSFORM encompasses the entire ENCODE(HASH()) operation:
TRANSFORM
ENCODE(HASH())
TRANSFORM(data) = ENCODE(HASH(data))
hex(MD5(hex(MD5(hex(MD5(data))))))
TRANSFORM(TRANSFORM(TRANSFORM(data)))
for (i = 0; i < nrounds; i++) { data = TRANSFORM(data) }
Using the above example case, you would configure mod_sql_passwd to perform multiple rounds of transformation using the SQLPasswordRounds directive, like so:
SQLAuthTypes MD5 SQLPasswordEncoding hex SQLPasswordRounds 3