mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
We currently have a convoluted and complex selection of which random numbers to use. We can simplify this down to two functions that cover all of our use cases: 1) Randomness for crypto: this one needs to wait until the RNG is initialized. So it uses getrandom(0). If that's not available, it polls on /dev/random, and then reads from /dev/urandom. This function returns whether or not it was successful, as before. 2) Randomness for other things: this one uses getrandom(GRND_INSECURE). If it's not available it uses getrandom(GRND_NONBLOCK). And if that would block, then it falls back to /dev/urandom. And if /dev/urandom isn't available, it uses the fallback code. It never fails and doesn't return a value. These two cases match all the uses of randomness inside of systemd. I would prefer to make both of these return void, and get rid of the fallback code, and simply assert in the incredibly unlikely case that /dev/urandom doesn't exist. But Luca disagrees, so this commit attempts to instead keep case (1) returning a return value, which all the callers already check, and fix the fallback code in (2) to be less bad than before. For the less bad fallback code for (2), we now use auxval and some timestamps, together with various counters representing the invocation, hash it all together and provide the output. Provided that AT_RANDOM is secure, this construction is probably okay too, though notably it doesn't have any forward secrecy. Fortunately, it's only used by random_bytes() and not by crypto_random_bytes().
212 lines
6.2 KiB
C
212 lines
6.2 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#if HAVE_CRYPT_H
|
|
/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
|
|
* removed from glibc at some point. As part of the removal, defines for
|
|
* crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
|
|
*
|
|
* Newer versions of glibc (v2.0+) already ship crypt.h with a definition
|
|
* of crypt(3) as well, so we simply include it if it is present. MariaDB,
|
|
* MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
|
|
* same way since ages without any problems.
|
|
*/
|
|
# include <crypt.h>
|
|
#else
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "errno-util.h"
|
|
#include "libcrypt-util.h"
|
|
#include "log.h"
|
|
#include "macro.h"
|
|
#include "memory-util.h"
|
|
#include "missing_stdlib.h"
|
|
#include "random-util.h"
|
|
#include "string-util.h"
|
|
#include "strv.h"
|
|
|
|
int make_salt(char **ret) {
|
|
|
|
#if HAVE_CRYPT_GENSALT_RA
|
|
const char *e;
|
|
char *salt;
|
|
|
|
/* If we have crypt_gensalt_ra() we default to the "preferred method" (i.e. usually yescrypt).
|
|
* crypt_gensalt_ra() is usually provided by libxcrypt. */
|
|
|
|
e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
|
|
if (!e)
|
|
#if HAVE_CRYPT_PREFERRED_METHOD
|
|
e = crypt_preferred_method();
|
|
#else
|
|
e = "$6$";
|
|
#endif
|
|
|
|
log_debug("Generating salt for hash prefix: %s", e);
|
|
|
|
salt = crypt_gensalt_ra(e, 0, NULL, 0);
|
|
if (!salt)
|
|
return -errno;
|
|
|
|
*ret = salt;
|
|
return 0;
|
|
#else
|
|
/* If crypt_gensalt_ra() is not available, we use SHA512 and generate the salt on our own. */
|
|
|
|
static const char table[] =
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"0123456789"
|
|
"./";
|
|
|
|
uint8_t raw[16];
|
|
char *salt, *j;
|
|
size_t i;
|
|
int r;
|
|
|
|
/* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
|
|
* SHA512, i.e. is legacy-free and minimizes our deps. */
|
|
|
|
assert_cc(sizeof(table) == 64U + 1U);
|
|
|
|
log_debug("Generating fallback salt for hash prefix: $6$");
|
|
|
|
/* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
|
|
r = crypto_random_bytes(raw, sizeof(raw));
|
|
if (r < 0)
|
|
return r;
|
|
|
|
salt = new(char, 3+sizeof(raw)+1+1);
|
|
if (!salt)
|
|
return -ENOMEM;
|
|
|
|
/* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
|
|
j = stpcpy(salt, "$6$");
|
|
for (i = 0; i < sizeof(raw); i++)
|
|
j[i] = table[raw[i] & 63];
|
|
j[i++] = '$';
|
|
j[i] = 0;
|
|
|
|
*ret = salt;
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#if HAVE_CRYPT_RA
|
|
# define CRYPT_RA_NAME "crypt_ra"
|
|
#else
|
|
# define CRYPT_RA_NAME "crypt_r"
|
|
|
|
/* Provide a poor man's fallback that uses a fixed size buffer. */
|
|
|
|
static char* systemd_crypt_ra(const char *phrase, const char *setting, void **data, int *size) {
|
|
assert(data);
|
|
assert(size);
|
|
|
|
/* We allocate the buffer because crypt(3) says: struct crypt_data may be quite large (32kB in this
|
|
* implementation of libcrypt; over 128kB in some other implementations). This is large enough that
|
|
* it may be unwise to allocate it on the stack. */
|
|
|
|
if (!*data) {
|
|
*data = new0(struct crypt_data, 1);
|
|
if (!*data) {
|
|
errno = -ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
*size = (int) (sizeof(struct crypt_data));
|
|
}
|
|
|
|
char *t = crypt_r(phrase, setting, *data);
|
|
if (!t)
|
|
return NULL;
|
|
|
|
/* crypt_r may return a pointer to an invalid hashed password on error. Our callers expect NULL on
|
|
* error, so let's just return that. */
|
|
if (t[0] == '*')
|
|
return NULL;
|
|
|
|
return t;
|
|
}
|
|
|
|
#define crypt_ra systemd_crypt_ra
|
|
|
|
#endif
|
|
|
|
int hash_password_full(const char *password, void **cd_data, int *cd_size, char **ret) {
|
|
_cleanup_free_ char *salt = NULL;
|
|
_cleanup_(erase_and_freep) void *_cd_data = NULL;
|
|
char *p;
|
|
int r, _cd_size = 0;
|
|
|
|
assert(!!cd_data == !!cd_size);
|
|
|
|
r = make_salt(&salt);
|
|
if (r < 0)
|
|
return log_debug_errno(r, "Failed to generate salt: %m");
|
|
|
|
errno = 0;
|
|
p = crypt_ra(password, salt, cd_data ?: &_cd_data, cd_size ?: &_cd_size);
|
|
if (!p)
|
|
return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)),
|
|
CRYPT_RA_NAME "() failed: %m");
|
|
|
|
p = strdup(p);
|
|
if (!p)
|
|
return -ENOMEM;
|
|
|
|
*ret = p;
|
|
return 0;
|
|
}
|
|
|
|
bool looks_like_hashed_password(const char *s) {
|
|
/* Returns false if the specified string is certainly not a hashed UNIX password. crypt(5) lists
|
|
* various hashing methods. We only reject (return false) strings which are documented to have
|
|
* different meanings.
|
|
*
|
|
* In particular, we allow locked passwords, i.e. strings starting with "!", including just "!",
|
|
* i.e. the locked empty password. See also fc58c0c7bf7e4f525b916e3e5be0de2307fef04e.
|
|
*/
|
|
if (!s)
|
|
return false;
|
|
|
|
s += strspn(s, "!"); /* Skip (possibly duplicated) locking prefix */
|
|
|
|
return !STR_IN_SET(s, "x", "*");
|
|
}
|
|
|
|
int test_password_one(const char *hashed_password, const char *password) {
|
|
_cleanup_(erase_and_freep) void *cd_data = NULL;
|
|
int cd_size = 0;
|
|
const char *k;
|
|
|
|
errno = 0;
|
|
k = crypt_ra(password, hashed_password, &cd_data, &cd_size);
|
|
if (!k) {
|
|
if (errno == ENOMEM)
|
|
return -ENOMEM;
|
|
/* Unknown or unavailable hashing method or string too short */
|
|
return 0;
|
|
}
|
|
|
|
return streq(k, hashed_password);
|
|
}
|
|
|
|
int test_password_many(char **hashed_password, const char *password) {
|
|
int r;
|
|
|
|
STRV_FOREACH(hpw, hashed_password) {
|
|
r = test_password_one(*hpw, password);
|
|
if (r < 0)
|
|
return r;
|
|
if (r > 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|