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
.
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:
mod_sql
.
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.
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.
<VirtualHost>
, <Global>
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:
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:
N=16384, r=8, p=1
This cost uses parameters where the value generated is considered very "sensitive".
For scrypt
, depending on the version of libsodium
used, this cost uses:
N=1048576, r=8, p=1
<VirtualHost>
, <Global>
The SQLPasswordEncoding
directive configures the encoding that
mod_sql_passwd
expects when handling password values retrieved
from a SQL database.
The following encoding values are currently supported:
If no SQLPasswordEncoding
directive is configured,
mod_sql_passwd
will use "hex" by default.
<VirtualHost>
, <Global>
The SQLPasswordEngine
directive enables or disables the module's
registered SQLAuthType
handlers.
<VirtualHost>
, <Global>
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.
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.
<VirtualHost>
, <Global>
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.
Note that PBKDF2 requires that a salt be available, e.g.
via SQLPasswordSaltFile
or
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.
Example:
# 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:
SQLNamedQuery get-user-pbkdf2 SELECT "algo, iter, len FROM user_pbkdf2 WHERE user = '%{0}' SQLPasswordPBKDF2 sql:/get-user-pbkdf2
<VirtualHost>
, <Global>
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.
See the transformations section for a fuller
description of how mod_sql_passwd
operates on the password and
salt data.
<VirtualHost>
, <Global>
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.
The following encoding values are currently supported:
If no SQLPasswordSaltEncoding
directive is configured,
mod_sql_passwd
will use "none" by default.
<VirtualHost>
, <Global>
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 Prependtells
mod_sql_passwd
to prepend the salt as a prefix, and:
SQLPasswordSaltFile /path/to/salt Appendwill cause the salt to be appended as a sufix. Note that the default behavior is to append the salt as a suffix.
If no SQLPasswordSaltFile
is configured, then no salting is done.
<VirtualHost>
, <Global>
The SQLPasswordScrypt
directive configures the length
of the calculated Scrypt output hash in bytes. The default length
is 32 bytes.
<VirtualHost>
, <Global>
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 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 Prependtells
mod_sql_passwd
to prepend the salt as a prefix, and:
SQLPasswordUserSalt name Append SQLPasswordUserSalt sql:/get-user-salt Appendwill cause the salt to be appended as a sufix. Note that the default behavior is to append the salt as a suffix.
mod_sql_passwd
module is distributed with ProFTPD. Simply
follow the normal steps for using third-party modules in proftpd. The
mod_sql_passwd
module requires OpenSSL support, so you must
use the --enable-openssl
configuration option. In addition,
if you have the libsodium
library installed, simply include the
libsodium
headers/libraries in the build command to enable
additional algorithms.
NOTE: it is important that mod_sql_passwd
appear
after mod_sql
in your --with-modules
configure
option:
$ ./configure --enable-openssl --with-modules=mod_sql:mod_sql_passwd ...To build
mod_sql_passwd
as a DSO module:
$ ./configure --enable-dso --enable-openssl --with-shared=mod_sql_passwdThen follow the usual steps:
$ 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 -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.
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:20This trace logging can generate large files; it is intended for debugging use only, and should be removed from any production configuration.
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
.
Thus if we use a configuration like:
SQLAuthTypes MD5 SQLPasswordEncoding hexThen
mod_sql_passwd
performs the following processing:
hex(MD5(data))in order to calculate the value that it will compare against the password value stored for the authenticating client.
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 + passwordor appended to the password, e.g.:
data = password + salt
Let's show a configuration which uses a prepended salt:
SQLAuthTypes MD5 SQLPasswordEncoding hex SQLPasswordSaltFile /path/to/salt.data PrependThis means that
mod_sql_passwd
would end up checking the
following computed value against the value in the database:
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:
SQLAuthTypes MD5 SQLPasswordEncoding hex SQLPasswordSaltFile /path/to/salt.data PrependLet's look at each of the
SQLPasswordOptions
in turn:
HashPassword
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)which assuming hex and MD5, means:
data = salt + MD5(password)
HashSalt
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) + passwordwhich assuming hex and MD5, means:
data = MD5(salt) + password
HashEncodePassword
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))which assuming hex and MD5, means:
data = salt + hex(MD5(password))Note: If no salt is present, this option will be ignored. Without a salt, this option is equivalent to adding another round of transformation, which is not an obvious side effect.
HashEncodeSalt
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)) + passwordwhich assuming hex and MD5, means:
data = hex(MD5(salt)) + password
Of course, these various options can be combined:
SQLPasswordOptions HashEncodePassword HashEncodeSaltwhich would cause the data on which
mod_sql_passwd
operates to
be constructed like so:
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(data) = ENCODE(HASH(data))Let's also assume that passwords are stored in your database using something like this:
hex(MD5(hex(MD5(hex(MD5(data))))))That means that the data value has gone through multiple rounds of the
TRANSFORM
function, e.g.:
TRANSFORM(TRANSFORM(TRANSFORM(data)))In this case, there are 3 rounds of transformation:
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 3The combination of
SQLPasswordOptions
and
SQLPasswordRounds
means that quite few combinations of password
values can be supported by the mod_sql_passwd
module.