Reject passwords that cannot be encrypted
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).
Reject passwords that cannot be encrypted
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).
libathemecore: rip out C99-snprintf compat code
We now rely heavily on a proper C99 compiler for the following (non-
exhaustive) list of features:
========================================================================
- C++-style single-line comments:
// This is a C99 comment that does not need a closing element
// These comments continue only to the end of the line they are on
========================================================================
- Variable declaration in for:
for (unsigned int i = 0; i < 4; i++)
========================================================================
- Designated initialisers:
struct foo {
unsigned int a;
unsigned int b;
};
static struct foo bar = {
.a = 4,
.b = 8,
};
========================================================================
- Variable declarations anywhere in a function:
void function(void)
{
(void) fprintf(stderr, "Hello, world!\n");
const unsigned int answer = 42;
(void) fprintf(stderr, "The answer to life, the universe, and "
"everything, is: %u\n", answer);
}
========================================================================
- Block-scope variable declarations:
void function(void)
{
if (some_condition)
{
const unsigned int x = 4;
(void) fprintf(stderr, "%u\n", x);
}
// x does not exist at this point
}
========================================================================
- The static qualifier in a function parameter list:
void foo(unsigned int arr[static 4])
{
// Would explode if passed an array with fewer than 4 elements.
for (unsigned int i = 0; i < 4; i++)
(void) fprintf(stderr, "%u ", arr[i]);
(void) fprintf(stderr, "\n");
}
void bar(unsigned int num[static 1])
{
/* Decays to "unsigned int *num" but also indicates that a NULL
* argument is not allowed. This would explode if passed NULL.
*/
(void) fprintf(stderr, "%u\n", num[0]);
}
void baz(void)
{
unsigned int arr[] = { 1, 2, 3 };
// Generates a compile-time error; 'arr' is not large enough:
(void) foo(arr);
// Likewise:
(void) bar(NULL);
}
========================================================================
- The restrict qualifier in a function parameter list:
void foo(unsigned int *restrict a, unsigned int *restrict b)
{
// Compiler is now free to re-order these two stores:
*a = 8;
*b = 4;
/* Saves emitting at least one load instruction.
* (Does not have to reload *a after assigning to *b)
* May always print 8 even if both pointers are the same!
*/
(void) fprintf(stderr, "%u\n", *a);
}
========================================================================
- Anonymous variables in function calls:
struct foo {
unsigned int a;
unsigned int b;
};
void bar(const struct foo *const foo)
{
(void) fprintf(stderr, "%u %u\n", foo->a, foo->b);
}
void baz(void)
{
// Prints 4 8
(void) bar((&(struct foo){ 4, 8 }));
}
========================================================================
Since this is now the case, a non-C99 compiler cannot build us anymore.
We therefore do not need the libathemecore/snprintf.c fallback file.
Additionally, that file has many shortcomings:
- If you are cross-compiling, it is always compiled in and used because
the m4 file does a run-time test to see if it is necessary and the
test (sensibly, I might add) always fails during a cross-compile.
- Due to the above, compiler optimisations (such as constant literal
propagation) and security features (such as source fortification) are
not applied to any (v)snprintf(3) calls if we are being cross-
compiled.
- This implementation does not support positional format tokens (%n$s),
which are necessary for translations.
Rip out the fallback implementation. Also confirm whether positional
format tokens work (which is not guaranteed), and disable localization
support if they don't.