--- BEGIN tl;dr ---
Assuming your Pure-FTPd is running on a Linux host with glibc 2.7+:
- Use
MySQLCrypt crypt
– without parentheses
Run this Python 3 one-liner on the same system where Pure-FTPd runs:
python3 -c 'import sys, crypt, getpass; print(crypt.crypt(getpass.getpass("Password: "), crypt.METHOD_SHA512))'
Enter the desired password.
- Copy and paste the crypt-ed password (starting with
$6$
) into the database.
--- END tl;dr ---
The idea behind non-plaintext MySQLCrypt
is to disable plaintext passwords altogether. The parenthesized MySQLCrypt crypt()
still accepting the hash as a plaintext password suggests that Pure-FTPd treated the string crypt()
as an unknown crypt method and ignored the directive altogether, using the default crypt method of plaintext instead. The un-parenthesized MySQLCrypt crypt
“breaking” plaintext logins is actually a step in the right direction: It disabled plaintext login and started treating passwords in the database as crypt-ed only.
Now, the question is how to properly hash a password into an acceptable form. According to src/mysql.c
of Pure-FTPd, MySQLCrypt crypt
would cause it to use the crypt()
function, whose acceptable forms depend on the implementation. For example, if you are running Pure-FTPd on a Linux machine with glibc 2.7 or higher, there are 4 forms accepted:
- SHA-512 (starts with
$6$
)
- SHA-256 (starts with
$5$
)
- MD5 (starts with
$1$
)
- Traditional DES (starts with an alphanumeric letter); should probably be avoided though
There are a few ways of invoking the system crypt()
; my preferred method is Python 3's built-in crypt
module. This short script would do our bidding:
import sys
from crypt import crypt, METHOD_SHA512
from getpass import getpass
print(crypt(getpass("Password: "), METHOD_SHA512))
Or if you prefer a triple-clickable, copy-and-paste-friendly one-liner:
python3 -c 'import sys, crypt, getpass; print(crypt.crypt(getpass.getpass("Password: "), crypt.METHOD_SHA512))'
A sample run:
root@ip-172-16-16-117:~# python3 -c 'import sys, crypt, getpass; print(crypt.crypt(getpass.getpass("Password: "), crypt.METHOD_SHA512))'
Password:
$6$vTbR62VMHKQNqnEk$TmfeMj/Q6G62RM.hi7liD0IrEvtUp2.jgXbfVRPone/sFTeOwJKftTrrW9j8Hd8.kJsF36OKwP4xHrnURGZTo/
It is imperative that this command be run on the same host where Pure-FTPd runs, in order to use the same crypt()
implementation.
The long line that starts with $6$
is the crypt-ed password. The $6$
prefix itself marks the password as hashed using SHA-512. If the system crypt()
does not support SHA-512, the output will not start $6$
; in this case, another algorithm should be used. For example, on macOS, a possible output is $6UCLzI8sPv16
. Note that it is excessively short and also lacks the dollar sign after $6
: This is because the macOS implementation of crypt()
does not support SHA-512.
Best Answer
Data encryption systems (typically) do not directly encrypt the data with the password. The problem with doing this is that when the password is changed, all the data needs to be re-encrypted, which is a very intensive process. (It also prevents supporting multiple passwords.) Instead, a random key is generated when the system is initialised, and the data is encrypted with this random key. The random key is then encrypted with the password and stored on disk. To decrypt the data the password is first used to decrypt the random key, and then the random key is used to decrypt the data. Changing the password simply involves re-encrypting this random key, and not all the data.
The encfs manpage mentions:
which implies that it uses this scheme.