Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

aaronmdjones
Copy link
Member

@aaronmdjones aaronmdjones commented Jan 26, 2025

At the moment, if services fails to encrypt a password, it will fall back to storing it in plain text in the database. This is clearly undesirable. This can happen for a couple of reasons off of the top of my head:

  • No encryption-capable crypto providers are loaded.

    This is extremely negligent, but is still possible. Services complains loudly every time it attempts to encrypt a password in this configuration, but that will not stop some negligent sysadmins from ignoring these messages and continuing to run it in this configuration.

  • pbkdf2v2 is the only encryption-capable crypto provider loaded, it is configured in SCRAM mode with GNU libidn for SASLprep normalisation, and the user supplies a password containing invalid UTF-8 byte sequences or ASCII control characters.

  • Either argon2 or scrypt are the only encryption-capable crypto provider loaded, and they are configured to use far more memory than the system has available; high enough to cause libc to fall back to a direct mmap(2), and high enough for the kernel's overcommit logic to reject it as extreme, returning NULL.

This is because set_password() has no way to indicate to its caller whether it was able to encrypt the password or not.

This commit forces set_password() to encrypt the password or return failure to its caller. The callers now check whether it was successful and abort their operation if it was not.

Also add a note to the SASL SCRAM documentation mentioning the prohibited password characters, and the same to the help files for commands that take passwords (but only if the SASL SCRAM module is loaded).

@aaronmdjones
Copy link
Member Author

The following test was performed with PBKDF2v2 in SCRAM mode being the only loaded crypto provider:

Normal password (0x68 0x75 0x6e 0x74 0x65 0x72 0x32):

[2025-01-26 13:07:08] -> :42XAAAAAB PRIVMSG 00AAAAAAC :SET PASSWORD hunter2
[2025-01-26 13:07:08] crypt_password: encrypted password with provider 'crypto/pbkdf2v2'
[2025-01-26 13:07:08] NickServ amdj/AAAAAAAAB:[email protected][0::1] SET:PASSWORD
[2025-01-26 13:07:08] <- :00AAAAAAC NOTICE 42XAAAAAB :The password for amdj has been successfully changed.
Green IRC coloured password (0x03 0x33 0x68 0x75 0x6e 0x74 0x65 0x72 0x32 0x03):

[2025-01-26 13:07:12] -> :42XAAAAAB PRIVMSG 00AAAAAAC :SET PASSWORD hunter2
[2025-01-26 13:07:12] atheme_pbkdf2v2_scram_normalize: Prohibited code points in input
[2025-01-26 13:07:12] atheme_pbkdf2v2_compute: SASLprep normalization of password failed
[2025-01-26 13:07:12] crypt_password: ci->crypt() failed for provider 'crypto/pbkdf2v2'
[2025-01-26 13:07:12] crypt_password: all encryption-capable crypto providers failed
[2025-01-26 13:07:12] set_password: failed to encrypt password for account 'amdj'
[2025-01-26 13:07:12] <- :00AAAAAAC NOTICE 42XAAAAAB :There was an error setting your password. Please check it for any invalid characters and contact network staff if the issue persists.

invpw

@aaronmdjones
Copy link
Member Author

The following test was performed with Argon2 being the only loaded crypto provider, configured with a memory cost of 29 (512 GiB of RAM) on a machine with only 128 GiB:

[2025-01-26 14:07:58] -> :42XAAAAAC PRIVMSG 00AAAAAAC :REGISTER letmein [email protected]
[2025-01-26 14:07:58] atheme_argon2_compute: argon2_ctx() failed: Memory allocation error
[2025-01-26 14:07:58] crypt_password: ci->crypt() failed for provider 'crypto/argon2'
[2025-01-26 14:07:58] crypt_password: all encryption-capable crypto providers failed
[2025-01-26 14:07:58] <- :00AAAAAAC NOTICE 42XAAAAAC :There was an error setting your password. Please check it for any invalid characters and contact network staff if the issue persists.

Screenshot from 2025-01-26 14-08-11

At the moment, if services fails to encrypt a password, it will fall back to
storing it in plain text in the database. This is clearly undesirable. This
can happen for a couple of reasons off of the top of my head:

- No encryption-capable crypto providers are loaded.

  This is extremely negligent, but is still possible. Services complains
  loudly every time it attempts to encrypt a password in this configuration,
  but that will not stop some negligent sysadmins from ignoring these
  messages and continuing to run it in this configuration.

- pbkdf2v2 is the only encryption-capable crypto provider loaded, it is
  configured in SCRAM mode with GNU libidn for SASLprep normalisation, and
  the user supplies a password containing invalid UTF-8 byte sequences or
  ASCII control characters.

- Either argon2 or scrypt are the only encryption-capable crypto provider
  loaded, and they are configured to use far more memory than the system has
  available; high enough to cause libc to fall back to a direct mmap(2), and
  high enough for the kernel's overcommit logic to reject it as extreme,
  returning NULL.

This is because set_password() has no way to indicate to its caller whether
it was able to encrypt the password or not.

This commit forces set_password() to encrypt the password or return failure
to its caller. The callers now check whether it was successful and abort
their operation if it was not.

Also add a note to the SASL SCRAM documentation mentioning the prohibited
password characters, and the same to the help files for commands that take
passwords (but only if the SASL SCRAM module is loaded).
@aaronmdjones aaronmdjones force-pushed the amdj/refuse-unencrypted-passwords branch from f1052f6 to 9c996b6 Compare January 26, 2025 14:52
This is a worthwhile modification to the verification logic which
should help ensure that plaintext passwords stay around in the
database for as little amount of time as possible.

It does have the slight disadvantage that this would then immediately
call back into the crypto module it just used to encrypt it in order to
verify it, which would have twice the computational overhead. However,
this would be a one-time cost for a single login attempt; after that, it
would be encrypted already, and then it's the same computational cost as
any other login attempt.

Reported-By: [email protected]
Posession of a valid SETPASS token is equivalent to having a means of
authentication, and we already log other authentication failures. Make
sure we log this one.

Reported-By: [email protected]
@aaronmdjones aaronmdjones force-pushed the amdj/refuse-unencrypted-passwords branch from a3a0b4a to f2d6719 Compare February 11, 2025 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant