Require libxcrypt >= 4.4.0 and drop libcrypt support (#38974)
This drops support of libcrypt provided by glibc, and always use libxcrypt. This also makes libxcrypt dlopen() dependency.
This commit is contained in:
@@ -212,7 +212,7 @@ REQUIREMENTS:
|
||||
newer though. TL;DR: turn audit off, still.
|
||||
|
||||
glibc >= 2.31
|
||||
libxcrypt or glibc (<= 2.38 built with --enable-crypt)
|
||||
libxcrypt >= 4.4.0 (optional)
|
||||
libmount >= 2.30 (from util-linux)
|
||||
(util-linux *must* be built without --enable-libmount-support-mtab)
|
||||
libseccomp >= 2.4.0 (optional)
|
||||
|
||||
+14
-25
@@ -685,15 +685,6 @@ conf.set('GPERF_LEN_TYPE', gperf_len_type,
|
||||
|
||||
#####################################################################
|
||||
|
||||
foreach header : [
|
||||
'crypt.h',
|
||||
]
|
||||
|
||||
if not cc.has_header(header)
|
||||
error(f'Header file @header@ not found')
|
||||
endif
|
||||
endforeach
|
||||
|
||||
foreach header : [
|
||||
'gshadow.h',
|
||||
'nss.h',
|
||||
@@ -1046,22 +1037,18 @@ else
|
||||
libatomic = []
|
||||
endif
|
||||
|
||||
libcrypt = dependency('libcrypt', 'libxcrypt', required : false)
|
||||
if not libcrypt.found()
|
||||
# fallback to use find_library() if libcrypt is provided by glibc, e.g. for LibreELEC.
|
||||
libcrypt = cc.find_library('crypt')
|
||||
if get_option('libc') == 'musl'
|
||||
libcrypt = []
|
||||
libcrypt_cflags = []
|
||||
have = get_option('libcrypt').allowed()
|
||||
else
|
||||
libcrypt = dependency('libcrypt', 'libxcrypt',
|
||||
required : get_option('libcrypt'),
|
||||
version : '>=4.4.0')
|
||||
libcrypt_cflags = libcrypt.partial_dependency(includes: true, compile_args: true)
|
||||
have = libcrypt.found()
|
||||
endif
|
||||
|
||||
foreach func : [
|
||||
'crypt_ra', # since libxcrypt-4.0.0
|
||||
'crypt_gensalt_ra', # since libxcrypt-4.0.0
|
||||
'crypt_preferred_method', # since libxcrypt-4.4.0
|
||||
]
|
||||
|
||||
have = cc.has_function(func, prefix : '''#include <crypt.h>''', args : '-D_GNU_SOURCE',
|
||||
dependencies : libcrypt)
|
||||
conf.set10('HAVE_' + func.to_upper(), have)
|
||||
endforeach
|
||||
conf.set10('HAVE_LIBCRYPT', have)
|
||||
|
||||
bpf_framework = get_option('bpf-framework')
|
||||
bpf_compiler = get_option('bpf-compiler')
|
||||
@@ -1594,10 +1581,11 @@ conf.set10('ENABLE_SYSUPDATED', have2)
|
||||
conf.set10('ENABLE_STORAGETM', get_option('storagetm'))
|
||||
|
||||
have = get_option('homed').require(
|
||||
conf.get('HAVE_LIBCRYPT') == 1 and
|
||||
conf.get('HAVE_OPENSSL') == 1 and
|
||||
conf.get('HAVE_LIBFDISK') == 1 and
|
||||
conf.get('HAVE_LIBCRYPTSETUP') == 1,
|
||||
error_message : 'openssl, fdisk and libcryptsetup required').allowed()
|
||||
error_message : 'libcrypt, openssl, fdisk, and libcryptsetup required').allowed()
|
||||
conf.set10('ENABLE_HOMED', have)
|
||||
|
||||
have = have and conf.get('HAVE_PAM') == 1
|
||||
@@ -3124,6 +3112,7 @@ foreach tuple : [
|
||||
['gnutls'],
|
||||
['libarchive'],
|
||||
['libbpf'],
|
||||
['libcrypt'],
|
||||
['libcryptsetup'],
|
||||
['libcryptsetup-plugins'],
|
||||
['libcurl'],
|
||||
|
||||
@@ -426,6 +426,8 @@ option('pwquality', type : 'feature', deprecated : { 'true' : 'enabled', 'false'
|
||||
description : 'libpwquality support')
|
||||
option('microhttpd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
|
||||
description : 'libµhttpd support')
|
||||
option('libcrypt', type : 'feature',
|
||||
description : 'libcrypt/libxcrypt support')
|
||||
option('libcryptsetup', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
|
||||
description : 'libcryptsetup support')
|
||||
option('libcryptsetup-plugins', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
|
||||
|
||||
@@ -6,6 +6,5 @@ executables += [
|
||||
'public' : true,
|
||||
'conditions' : ['ENABLE_FIRSTBOOT'],
|
||||
'sources' : files('firstboot.c'),
|
||||
'dependencies' : libcrypt,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -64,7 +64,6 @@ executables += [
|
||||
'sources' : systemd_homed_sources,
|
||||
'extract' : systemd_homed_extract_sources,
|
||||
'dependencies' : [
|
||||
libcrypt,
|
||||
libm,
|
||||
libopenssl,
|
||||
threads,
|
||||
@@ -80,7 +79,6 @@ executables += [
|
||||
],
|
||||
'dependencies' : [
|
||||
libblkid_cflags,
|
||||
libcrypt,
|
||||
libfdisk,
|
||||
libopenssl,
|
||||
libp11kit_cflags,
|
||||
@@ -93,7 +91,6 @@ executables += [
|
||||
'sources' : homectl_sources,
|
||||
'objects' : ['systemd-homed'],
|
||||
'dependencies' : [
|
||||
libcrypt,
|
||||
libdl,
|
||||
libopenssl,
|
||||
libp11kit_cflags,
|
||||
@@ -112,7 +109,6 @@ modules += [
|
||||
'conditions' : ['HAVE_PAM'],
|
||||
'sources' : pam_systemd_home_sources,
|
||||
'dependencies' : [
|
||||
libcrypt,
|
||||
libintl,
|
||||
libpam_misc,
|
||||
libpam,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include_next <crypt.h>
|
||||
|
||||
const char* missing_crypt_preferred_method(void);
|
||||
#define crypt_preferred_method missing_crypt_preferred_method
|
||||
|
||||
char* missing_crypt_gensalt_ra(const char *prefix, unsigned long count, const char *rbytes, int nrbytes);
|
||||
#define crypt_gensalt_ra missing_crypt_gensalt_ra
|
||||
|
||||
char* missing_crypt_ra(const char *phrase, const char *setting, void **data, int *size);
|
||||
#define crypt_ra missing_crypt_ra
|
||||
@@ -0,0 +1,111 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <assert.h>
|
||||
#include <crypt.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/random.h>
|
||||
|
||||
const char* missing_crypt_preferred_method(void) {
|
||||
return "$6$";
|
||||
}
|
||||
|
||||
char* missing_crypt_gensalt_ra(const char *prefix, unsigned long count, const char *rbytes, int nrbytes) {
|
||||
static const char table[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
"./";
|
||||
|
||||
static_assert(sizeof(table) == 64U + 1U);
|
||||
|
||||
/* This doesn't do anything but SHA512, and silently ignore all arguments, to make it legacy-free and
|
||||
* minimize the implementation. */
|
||||
|
||||
/* Insist on the best randomness by getrandom(), this is about keeping passwords secret after all. */
|
||||
uint8_t raw[16];
|
||||
for (size_t i = 0; i < sizeof(raw);) {
|
||||
size_t n = sizeof(raw) - i;
|
||||
ssize_t l = getrandom(raw + i, n, 0);
|
||||
if (l < 0)
|
||||
return NULL;
|
||||
if (l == 0) {
|
||||
/* Weird, should never happen. */
|
||||
errno = EIO;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((size_t) l == n)
|
||||
break; /* Done reading, success. */
|
||||
|
||||
i += l;
|
||||
/* Interrupted by a signal; keep going. */
|
||||
}
|
||||
|
||||
/* "$6$" + salt + "$" + NUL */
|
||||
char *salt = malloc(3 + sizeof(raw) + 1 + 1);
|
||||
if (!salt) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
|
||||
char *p = stpcpy(salt, "$6$");
|
||||
for (size_t i = 0; i < sizeof(raw); i++)
|
||||
*p++ = table[raw[i] & 63];
|
||||
*p++ = '$';
|
||||
*p = '\0';
|
||||
|
||||
return salt;
|
||||
}
|
||||
|
||||
char* missing_crypt_ra(const char *phrase, const char *setting, void **data, int *size) {
|
||||
struct crypt_data *buf = NULL;
|
||||
bool allocated = false;
|
||||
|
||||
if (!phrase || !setting || !data || !size) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*data) {
|
||||
if (*size != sizeof(struct crypt_data)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = *data;
|
||||
} else {
|
||||
if (*size != 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = calloc(1, sizeof(struct crypt_data));
|
||||
if (!buf) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
allocated = true;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
char *t = crypt_r(phrase, setting, buf);
|
||||
if (!t || t[0] == '*') {
|
||||
if (allocated)
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (allocated) {
|
||||
*data = buf;
|
||||
*size = sizeof(struct crypt_data);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ if get_option('libc') != 'musl'
|
||||
endif
|
||||
|
||||
libc_wrapper_sources += files(
|
||||
'crypt.c',
|
||||
'getopt.c',
|
||||
'printf.c',
|
||||
'stdio.c',
|
||||
|
||||
+116
-132
@@ -1,145 +1,160 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <crypt.h>
|
||||
#include <stdlib.h>
|
||||
#if HAVE_LIBCRYPT
|
||||
# include <crypt.h>
|
||||
#endif
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dlfcn-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "libcrypt-util.h"
|
||||
#include "log.h"
|
||||
#include "random-util.h" /* IWYU pragma: keep */
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
int make_salt(char **ret) {
|
||||
#if HAVE_LIBCRYPT
|
||||
static void *libcrypt_dl = NULL;
|
||||
|
||||
#if HAVE_CRYPT_GENSALT_RA
|
||||
static DLSYM_PROTOTYPE(crypt_gensalt_ra) = NULL;
|
||||
static DLSYM_PROTOTYPE(crypt_preferred_method) = NULL;
|
||||
static DLSYM_PROTOTYPE(crypt_ra) = NULL;
|
||||
|
||||
int dlopen_libcrypt(void) {
|
||||
#ifdef __GLIBC__
|
||||
static int cached = 0;
|
||||
int r;
|
||||
|
||||
if (libcrypt_dl)
|
||||
return 0; /* Already loaded */
|
||||
|
||||
if (cached < 0)
|
||||
return cached; /* Already tried, and failed. */
|
||||
|
||||
/* Several distributions like Debian/Ubuntu and OpenSUSE provide libxcrypt as libcrypt.so.1,
|
||||
* while others like Fedora/CentOS and Arch provide it as libcrypt.so.2. */
|
||||
ELF_NOTE_DLOPEN("crypt",
|
||||
"Support for hashing passwords",
|
||||
ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
|
||||
"libcrypt.so.2", "libcrypt.so.1");
|
||||
|
||||
_cleanup_(dlclosep) void *dl = NULL;
|
||||
r = dlopen_safe("libcrypt.so.2", &dl, /* reterr_dlerror= */ NULL);
|
||||
if (r < 0) {
|
||||
const char *dle = NULL;
|
||||
r = dlopen_safe("libcrypt.so.1", &dl, &dle);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "libcrypt.so.2/libcrypt.so.1 is not available: %s", dle ?: STRERROR(r));
|
||||
return (cached = -EOPNOTSUPP); /* turn into recognizable error */
|
||||
}
|
||||
log_debug("Loaded 'libcrypt.so.1' via dlopen()");
|
||||
} else
|
||||
log_debug("Loaded 'libcrypt.so.2' via dlopen()");
|
||||
|
||||
r = dlsym_many_or_warn(
|
||||
dl, LOG_DEBUG,
|
||||
DLSYM_ARG(crypt_gensalt_ra),
|
||||
DLSYM_ARG(crypt_preferred_method),
|
||||
DLSYM_ARG(crypt_ra));
|
||||
if (r < 0)
|
||||
return (cached = r);
|
||||
|
||||
libcrypt_dl = TAKE_PTR(dl);
|
||||
#else
|
||||
libcrypt_dl = NULL;
|
||||
sym_crypt_gensalt_ra = missing_crypt_gensalt_ra;
|
||||
sym_crypt_preferred_method = missing_crypt_preferred_method;
|
||||
sym_crypt_ra = missing_crypt_ra;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int make_salt(char **ret) {
|
||||
const char *e;
|
||||
char *salt;
|
||||
int r;
|
||||
|
||||
/* If we have crypt_gensalt_ra() we default to the "preferred method" (i.e. usually yescrypt).
|
||||
* crypt_gensalt_ra() is usually provided by libxcrypt. */
|
||||
assert(ret);
|
||||
|
||||
r = dlopen_libcrypt();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
|
||||
if (!e)
|
||||
#if HAVE_CRYPT_PREFERRED_METHOD
|
||||
e = crypt_preferred_method();
|
||||
#else
|
||||
e = "$6$";
|
||||
#endif
|
||||
e = sym_crypt_preferred_method();
|
||||
|
||||
log_debug("Generating salt for hash prefix: %s", e);
|
||||
|
||||
salt = crypt_gensalt_ra(e, 0, NULL, 0);
|
||||
salt = sym_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) {
|
||||
int hash_password(const char *password, char **ret) {
|
||||
_cleanup_free_ char *salt = NULL;
|
||||
_cleanup_(erase_and_freep) void *_cd_data = NULL;
|
||||
_cleanup_(erase_and_freep) void *cd_data = NULL;
|
||||
const char *p;
|
||||
int r, _cd_size = 0;
|
||||
int r, cd_size = 0;
|
||||
|
||||
assert(!!cd_data == !!cd_size);
|
||||
assert(password);
|
||||
assert(ret);
|
||||
|
||||
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);
|
||||
p = sym_crypt_ra(password, salt, &cd_data, &cd_size);
|
||||
if (!p)
|
||||
return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)),
|
||||
CRYPT_RA_NAME "() failed: %m");
|
||||
return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)), "crypt_ra() failed: %m");
|
||||
|
||||
return strdup_to(ret, p);
|
||||
}
|
||||
|
||||
int test_password_one(const char *hashed_password, const char *password) {
|
||||
_cleanup_(erase_and_freep) void *cd_data = NULL;
|
||||
int r, cd_size = 0;
|
||||
const char *k;
|
||||
|
||||
assert(hashed_password);
|
||||
assert(password);
|
||||
|
||||
r = dlopen_libcrypt();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
errno = 0;
|
||||
k = sym_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;
|
||||
|
||||
assert(password);
|
||||
|
||||
STRV_FOREACH(hpw, hashed_password) {
|
||||
r = test_password_one(*hpw, password);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
@@ -155,34 +170,3 @@ bool looks_like_hashed_password(const char *s) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,21 @@
|
||||
|
||||
#include "shared-forward.h"
|
||||
|
||||
#if HAVE_LIBCRYPT
|
||||
int dlopen_libcrypt(void);
|
||||
int make_salt(char **ret);
|
||||
int hash_password_full(const char *password, void **cd_data, int *cd_size, char **ret);
|
||||
static inline int hash_password(const char *password, char **ret) {
|
||||
return hash_password_full(password, NULL, NULL, ret);
|
||||
}
|
||||
bool looks_like_hashed_password(const char *s);
|
||||
int hash_password(const char *password, char **ret);
|
||||
int test_password_one(const char *hashed_password, const char *password);
|
||||
int test_password_many(char **hashed_password, const char *password);
|
||||
|
||||
#else
|
||||
|
||||
static inline int dlopen_libcrypt(void) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int hash_password(const char *password, char **ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool looks_like_hashed_password(const char *s);
|
||||
|
||||
@@ -367,7 +367,7 @@ libshared_deps = [threads,
|
||||
libaudit_cflags,
|
||||
libblkid_cflags,
|
||||
libbpf_cflags,
|
||||
libcrypt,
|
||||
libcrypt_cflags,
|
||||
libcryptsetup_cflags,
|
||||
libdl,
|
||||
libdw_cflags,
|
||||
|
||||
@@ -345,7 +345,7 @@ executables += [
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-libcrypt-util.c'),
|
||||
'dependencies' : libcrypt,
|
||||
'conditions' : ['HAVE_LIBCRYPT'],
|
||||
'timeout' : 120,
|
||||
},
|
||||
test_template + {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "idn-util.h"
|
||||
#include "libarchive-util.h"
|
||||
#include "libaudit-util.h"
|
||||
#include "libcrypt-util.h"
|
||||
#include "libfido2-util.h"
|
||||
#include "libmount-util.h"
|
||||
#include "main-func.h"
|
||||
@@ -52,6 +53,7 @@ static int run(int argc, char **argv) {
|
||||
ASSERT_DLOPEN(dlopen_libarchive, HAVE_LIBARCHIVE);
|
||||
ASSERT_DLOPEN(dlopen_libaudit, HAVE_AUDIT);
|
||||
ASSERT_DLOPEN(dlopen_libblkid, HAVE_BLKID);
|
||||
ASSERT_DLOPEN(dlopen_libcrypt, HAVE_LIBCRYPT);
|
||||
ASSERT_DLOPEN(dlopen_libfido2, HAVE_LIBFIDO2);
|
||||
ASSERT_DLOPEN(dlopen_libkmod, HAVE_KMOD);
|
||||
ASSERT_DLOPEN(dlopen_libmount, HAVE_LIBMOUNT);
|
||||
|
||||
@@ -1,41 +1,25 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <crypt.h>
|
||||
|
||||
#include "libcrypt-util.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
|
||||
static void test_crypt_preferred_method(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
log_info("crypt_preferred_method: %s",
|
||||
#if HAVE_CRYPT_PREFERRED_METHOD
|
||||
crypt_preferred_method()
|
||||
#else
|
||||
"(not available)"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
static void test_make_salt(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
TEST(make_salt) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
_cleanup_free_ char *t;
|
||||
|
||||
assert_se(make_salt(&t) == 0);
|
||||
ASSERT_OK(make_salt(&t));
|
||||
log_info("%s", t);
|
||||
|
||||
ASSERT_FALSE(strv_contains(l, t));
|
||||
ASSERT_OK(strv_consume(&l, TAKE_PTR(t)));
|
||||
}
|
||||
}
|
||||
|
||||
static int test_hash_password(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
TEST(hash_password) {
|
||||
/* As a warm-up exercise, check if we can hash passwords. */
|
||||
|
||||
bool have_sane_hash = false;
|
||||
|
||||
FOREACH_STRING(hash,
|
||||
"ew3bU1.hoKk4o",
|
||||
"$1$gc5rWpTB$wK1aul1PyBn9AX1z93stk1",
|
||||
@@ -43,80 +27,46 @@ static int test_hash_password(void) {
|
||||
"$5$lGhDrcrao9zb5oIK$05KlOVG3ocknx/ThreqXE/gk.XzFFBMTksc4t2CPDUD",
|
||||
"$6$c7wB/3GiRk0VHf7e$zXJ7hN0aLZapE.iO4mn/oHu6.prsXTUG/5k1AxpgR85ELolyAcaIGRgzfwJs3isTChMDBjnthZyaMCfCNxo9I.",
|
||||
"$y$j9T$$9cKOWsAm4m97WiYk61lPPibZpy3oaGPIbsL4koRe/XD") {
|
||||
int b;
|
||||
|
||||
b = test_password_one(hash, "ppp");
|
||||
log_info("%s: %s", hash, yes_no(b));
|
||||
#if defined(XCRYPT_VERSION_MAJOR)
|
||||
/* xcrypt is supposed to always implement all methods. */
|
||||
assert_se(b);
|
||||
#ifndef __GLIBC__
|
||||
/* musl does not support yescrypt. */
|
||||
if (hash[1] == 'y') {
|
||||
ASSERT_OK_ZERO(test_password_one(hash, "ppp"));
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (b && IN_SET(hash[1], '6', 'y'))
|
||||
have_sane_hash = true;
|
||||
ASSERT_OK_POSITIVE(test_password_one(hash, "ppp"));
|
||||
}
|
||||
|
||||
return have_sane_hash;
|
||||
}
|
||||
FOREACH_STRING(i, "abc123", "h⸿sło") {
|
||||
_cleanup_free_ char *hashed;
|
||||
|
||||
static void test_hash_password_full(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
ASSERT_OK(hash_password(i, &hashed));
|
||||
log_debug("\"%s\" → \"%s\"", i, hashed);
|
||||
|
||||
_cleanup_free_ void *cd_data = NULL;
|
||||
int cd_size = 0;
|
||||
|
||||
log_info("sizeof(struct crypt_data): %zu bytes", sizeof(struct crypt_data));
|
||||
|
||||
for (unsigned c = 0; c < 2; c++)
|
||||
FOREACH_STRING(i, "abc123", "h⸿sło") {
|
||||
_cleanup_free_ char *hashed;
|
||||
|
||||
if (c == 0)
|
||||
assert_se(hash_password_full(i, &cd_data, &cd_size, &hashed) == 0);
|
||||
else
|
||||
assert_se(hash_password_full(i, NULL, NULL, &hashed) == 0);
|
||||
log_debug("\"%s\" → \"%s\"", i, hashed);
|
||||
log_info("crypt_r[a] buffer size: %i bytes", cd_size);
|
||||
|
||||
assert_se(test_password_one(hashed, i) == true);
|
||||
assert_se(test_password_one(i, hashed) <= 0); /* We get an error for non-utf8 */
|
||||
assert_se(test_password_one(hashed, "foobar") == false);
|
||||
assert_se(test_password_many(STRV_MAKE(hashed), i) == true);
|
||||
assert_se(test_password_many(STRV_MAKE(hashed), "foobar") == false);
|
||||
assert_se(test_password_many(STRV_MAKE(hashed, hashed, hashed), "foobar") == false);
|
||||
assert_se(test_password_many(STRV_MAKE("$y$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH",
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
i) == true);
|
||||
assert_se(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
i) == true);
|
||||
assert_se(test_password_many(STRV_MAKE("$y$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH",
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
"") == false);
|
||||
assert_se(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
"") == false);
|
||||
ASSERT_OK_POSITIVE(test_password_one(hashed, i));
|
||||
ASSERT_LE(test_password_one(i, hashed), 0); /* We get an error for non-utf8 */
|
||||
ASSERT_OK_ZERO(test_password_one(hashed, "foobar"));
|
||||
ASSERT_OK_POSITIVE(test_password_many(STRV_MAKE(hashed), i));
|
||||
ASSERT_OK_ZERO(test_password_many(STRV_MAKE(hashed), "foobar"));
|
||||
ASSERT_OK_ZERO(test_password_many(STRV_MAKE(hashed, hashed, hashed), "foobar"));
|
||||
ASSERT_OK_POSITIVE(test_password_many(STRV_MAKE("$y$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH",
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
i));
|
||||
ASSERT_OK_POSITIVE(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
i));
|
||||
ASSERT_OK_ZERO(test_password_many(STRV_MAKE("$y$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH",
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
""));
|
||||
ASSERT_OK_ZERO(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
|
||||
hashed,
|
||||
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
|
||||
""));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
#if defined(__powerpc__) && !defined(XCRYPT_VERSION_MAJOR)
|
||||
return log_tests_skipped("crypt_r() causes a buffer overflow on ppc64el, see https://github.com/systemd/systemd/pull/16981#issuecomment-691203787");
|
||||
#endif
|
||||
|
||||
test_crypt_preferred_method();
|
||||
test_make_salt();
|
||||
|
||||
if (!test_hash_password())
|
||||
return log_tests_skipped("crypt doesn't support yescrypt or sha512crypt");
|
||||
|
||||
test_hash_password_full();
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "format-util.h"
|
||||
#include "libcrypt-util.h"
|
||||
#include "log.h"
|
||||
#include "memory-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -342,18 +341,6 @@ TEST(get_group_creds) {
|
||||
test_get_group_creds_one("65534", NOBODY_GROUP_NAME, GID_NOBODY);
|
||||
}
|
||||
|
||||
TEST(make_salt) {
|
||||
_cleanup_free_ char *s, *t;
|
||||
|
||||
ASSERT_OK(make_salt(&s));
|
||||
log_info("got %s", s);
|
||||
|
||||
ASSERT_OK(make_salt(&t));
|
||||
log_info("got %s", t);
|
||||
|
||||
ASSERT_NOT_STREQ(s, t);
|
||||
}
|
||||
|
||||
TEST(in_gid) {
|
||||
ASSERT_OK(in_gid(getgid()));
|
||||
ASSERT_OK(in_gid(getegid()));
|
||||
|
||||
Reference in New Issue
Block a user