mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-28 20:27:03 +00:00
Analysis of customer stalls revealed that the function `detail::hash_with_salt` (invoked by `passwords::check`) often blocks the reactor. Internally, this function uses the external `crypt_r` function to compute password hashes, which is CPU-intensive.
This PR addresses the issue in two ways:
1) `sha-512` is now the only password hashing scheme for new passwords (it was already the common-case).
2) `passwords::check` is moved to a dedicated alien thread.
Regarding point 1: before this change, the following hashing schemes were supported by `identify_best_supported_scheme()`: bcrypt_y, bcrypt_a, SHA-512, SHA-256, and MD5. The reason for this was that the `crypt_r` function used for password hashing comes from an external library (currently `libxcrypt`), and the supported hashing algorithms vary depending on the library in use. However:
- The bcrypt schemes never worked properly because their prefixes lack the required round count (e.g. `$2y$` instead of `$2y$05$`). Moreover, bcrypt is slower than SHA-512, so it not good idea to fix or use it.
- SHA-256 and SHA-512 both belong to the SHA-2 family. Libraries that support one almost always support the other, so it’s very unlikely to find SHA-256 without SHA-512.
- MD5 is no longer considered secure for password hashing.
Regarding point 2: the `passwords::check` call now runs on a shared alien thread created at database startup. An `std::mutex` synchronizes that thread with the shards. In theory this could introduce a frequent lock contention, but in practice each shard handles only a few hundred new connections per second—even during storms. There is already `_conns_cpu_concurrency_semaphore` in `generic_server` limits the number of concurrent connection handlers.
Fixes https://github.com/scylladb/scylladb/issues/24524
Backport not needed, as it is a new feature.
Closes scylladb/scylladb#24924
* github.com:scylladb/scylladb:
main: utils: add thread names to alien workers
auth: move passwords::check call to alien thread
test: wait for 3 clients with given username in test_service_level_api
auth: refactor password checking in password_authenticator
auth: make SHA-512 the only password hashing scheme for new passwords
auth: whitespace change in identify_best_supported_scheme()
auth: require scheme as parameter for `generate_salt`
auth: check password hashing scheme support on authenticator start
(cherry picked from commit c762425ea7)
113 lines
3.4 KiB
C++
113 lines
3.4 KiB
C++
/*
|
|
* Copyright (C) 2018-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <random>
|
|
#include <stdexcept>
|
|
|
|
#include <seastar/core/sstring.hh>
|
|
|
|
#include "seastarx.hh"
|
|
|
|
namespace auth::passwords {
|
|
|
|
class no_supported_schemes : public std::runtime_error {
|
|
public:
|
|
no_supported_schemes();
|
|
};
|
|
///
|
|
/// Apache Cassandra uses a library to provide the bcrypt scheme. In ScyllaDB, we use SHA-512
|
|
/// instead of bcrypt for performance and for historical reasons (see scylladb#24524).
|
|
/// Currently, SHA-512 is always chosen as the hashing scheme for new passwords, but the other
|
|
/// algorithms remain supported for CREATE ROLE WITH HASHED PASSWORD and backward compatibility.
|
|
///
|
|
enum class scheme {
|
|
bcrypt_y,
|
|
bcrypt_a,
|
|
sha_512,
|
|
sha_256,
|
|
md5
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
template <typename RandomNumberEngine>
|
|
sstring generate_random_salt_bytes(RandomNumberEngine& g) {
|
|
static const sstring valid_bytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
|
|
static constexpr std::size_t num_bytes = 16;
|
|
std::uniform_int_distribution<std::size_t> dist(0, valid_bytes.size() - 1);
|
|
sstring result(num_bytes, 0);
|
|
|
|
for (char& c : result) {
|
|
c = valid_bytes[dist(g)];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
///
|
|
/// Test given hashing scheme on the current system.
|
|
///
|
|
/// \throws \ref no_supported_schemes when scheme is unsupported.
|
|
///
|
|
void verify_scheme(scheme scheme);
|
|
|
|
std::string_view prefix_for_scheme(scheme) noexcept;
|
|
|
|
///
|
|
/// Generate a implementation-specific salt string for hashing passwords.
|
|
///
|
|
/// The `RandomNumberEngine` is used to generate the string, which is an implementation-specific length.
|
|
///
|
|
/// \throws \ref no_supported_schemes when no known hashing schemes are supported on the system.
|
|
///
|
|
template <typename RandomNumberEngine>
|
|
sstring generate_salt(RandomNumberEngine& g, scheme scheme) {
|
|
static const sstring prefix = sstring(prefix_for_scheme(scheme));
|
|
return prefix + generate_random_salt_bytes(g);
|
|
}
|
|
|
|
///
|
|
/// Hash a password combined with an implementation-specific salt string.
|
|
///
|
|
/// \throws \ref std::system_error when an unexpected implementation-specific error occurs.
|
|
///
|
|
sstring hash_with_salt(const sstring& pass, const sstring& salt);
|
|
|
|
} // namespace detail
|
|
|
|
///
|
|
/// Run a one-way hashing function on cleartext to produce encrypted text.
|
|
///
|
|
/// Prior to applying the hashing function, random salt is amended to the cleartext. The random salt bytes are generated
|
|
/// according to the random number engine `g`.
|
|
///
|
|
/// The result is the encrypted ciphertext, and also the salt used but in a implementation-specific format.
|
|
///
|
|
/// \throws \ref std::system_error when the implementation-specific implementation fails to hash the cleartext.
|
|
///
|
|
template <typename RandomNumberEngine>
|
|
sstring hash(const sstring& pass, RandomNumberEngine& g, scheme scheme) {
|
|
return detail::hash_with_salt(pass, detail::generate_salt(g, scheme));
|
|
}
|
|
|
|
///
|
|
/// Check that cleartext matches previously hashed cleartext with salt.
|
|
///
|
|
/// \ref salted_hash is the result of invoking \ref hash, which is the implementation-specific combination of the hashed
|
|
/// password and the salt that was generated for it.
|
|
///
|
|
/// \returns `true` if the cleartext matches the salted hash.
|
|
///
|
|
/// \throws \ref std::system_error when an unexpected implementation-specific error occurs.
|
|
///
|
|
bool check(const sstring& pass, const sstring& salted_hash);
|
|
|
|
} // namespace auth::passwords
|