1
0
mirror of https://github.com/systemd/systemd synced 2026-03-19 11:34:46 +01:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Lennart Poettering
5fcca42f18 update TODO 2026-01-02 11:20:23 +01:00
Daan De Meyer
944ae1286b tree-wide: Use pamh as pam_handle_t parameter name
libpam uses pamh in its function declarations for
the plugin API so let's use the same name in our
tree as well.

Making sure the plugin function definitions match
the plugin function declarations is required to
enable clang-tidy's
readability-inconsistent-declaration-parameter-name
check, but to keep things consistent everywhere we
opt to use pamh tree-wide.
2026-01-02 09:21:43 +01:00
Yu Watanabe
2553c53730 tools: drop unnecessary sys/capability.h header
After 9b414a38fadb41c9ea056ed5d284ab5098251a37 (#39425), the header is
not required. And after b295c166f94526aae830893612a1584840f2f087, the
header is not installed in CI environments.
2026-01-02 08:46:05 +01:00
Yu Watanabe
6d2c9c2203 tools: show each command to make it easier to debug 2026-01-02 08:46:05 +01:00
Yu Watanabe
fe3c790f71 tools: allow to run setup-musl-build.sh for already set up directory 2026-01-02 08:46:05 +01:00
Yu Watanabe
508dc4c3fa
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.
2026-01-02 14:32:12 +09:00
Yu Watanabe
d0b224f845 libcrypt-util: turn into dlopen() dependency
Note, this drops logging only test case for crypt_preferred_method(),
as that requires explicitly dlopen() the library. But, we should test
that make_salt() and friends automatically dlopen() it.
2026-01-02 13:06:14 +09:00
Yu Watanabe
8de783a288 libcrypt: allow to build systemd without libcrypt/libxcrypt
libcrypt is only used by firstboot, homed, and sysusers, which can be
disabled by meson option.
Let's not require the library unconditionally.
2026-01-02 12:55:57 +09:00
Yu Watanabe
5863641bb7 Require libxcrypt-4.4.0 or newer and drop support of libcrypt
libcrypt was no longer built by default since glibc-2.38, and it has been
completely removed since glibc-2.39.

Let's always use libxcrypt, unless when building with musl. As already
major distribution already have libxcrypt-4.4.x, hence let's also bump
the required minimum version to 4.4.0.

libxcrypt cannot be built with musl, hence the previous fallback logic
in libcrypt-util.c are moved to musl/crypt.c.

Note, libxcrypt-4.4.0 was released on 2018-11-20.
See also #38608.
2026-01-02 12:55:53 +09:00
Yu Watanabe
8608d2e910 test-libcrypt-util: use DEFINE_TEST_MAIN() and ASSERT_XYZ()
Also, tests for make_salt() in test-user-util.c are moved to
test-libcrypt-util.c.
2026-01-02 12:54:01 +09:00
Yu Watanabe
d5267b2275 libcrypt-util: add missing assertions 2026-01-02 12:54:01 +09:00
Yu Watanabe
b26406c9c3 libcrypt-util: drop unused hash_passwrod_full()
It is only used by test cases. Not necessary to keep it.
2026-01-02 12:54:00 +09:00
Yu Watanabe
d274748160 libcrypt-util: move looks_like_hashed_password()
No functional change, just preparation for later change.
2026-01-02 12:54:00 +09:00
23 changed files with 872 additions and 838 deletions

2
README
View File

@ -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)

4
TODO
View File

@ -137,6 +137,10 @@ Deprecations and removals:
Features:
* systemd-sysext: add "exec" command or so that is a bit like "refresh" but
runs it in a new namespace and then just executes the selected binary within
it. Could be useful to run one-off binaries inside a sysext as a CLI tool.
* systemd-repart: implement Integrity=data/meta and Integrity=inline for non-LUKS
case. Currently, only Integrity=inline combined with Encrypt= is implemented
and uses libcryptsetup features. Add support for plain dm-integrity setups when

View File

@ -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'],

View File

@ -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' },

View File

@ -1192,18 +1192,18 @@ static int ask_password_conv(
return PAM_SUCCESS;
}
static int pam_close_session_and_delete_credentials(pam_handle_t *handle, int flags) {
static int pam_close_session_and_delete_credentials(pam_handle_t *pamh, int flags) {
int r, s;
assert(handle);
assert(pamh);
r = sym_pam_close_session(handle, flags);
r = sym_pam_close_session(pamh, flags);
if (r != PAM_SUCCESS)
pam_syslog_pam_error(handle, LOG_DEBUG, r, "pam_close_session() failed: @PAMERR@");
pam_syslog_pam_error(pamh, LOG_DEBUG, r, "pam_close_session() failed: @PAMERR@");
s = sym_pam_setcred(handle, PAM_DELETE_CRED | flags);
s = sym_pam_setcred(pamh, PAM_DELETE_CRED | flags);
if (s != PAM_SUCCESS)
pam_syslog_pam_error(handle, LOG_DEBUG, r, "pam_setcred(PAM_DELETE_CRED) failed: @PAMERR@");
pam_syslog_pam_error(pamh, LOG_DEBUG, r, "pam_setcred(PAM_DELETE_CRED) failed: @PAMERR@");
return r != PAM_SUCCESS ? r : s;
}
@ -1339,7 +1339,7 @@ static int setup_pam(
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
_cleanup_strv_free_ char **e = NULL;
_cleanup_free_ char *tty = NULL;
pam_handle_t *handle = NULL;
pam_handle_t *pamh = NULL;
sigset_t old_ss;
int pam_code = PAM_SUCCESS, r;
bool close_session = false;
@ -1369,9 +1369,9 @@ static int setup_pam(
if (log_get_max_level() < LOG_DEBUG)
flags |= PAM_SILENT;
pam_code = sym_pam_start(context->pam_name, user, &conv, &handle);
pam_code = sym_pam_start(context->pam_name, user, &conv, &pamh);
if (pam_code != PAM_SUCCESS) {
handle = NULL;
pamh = NULL;
goto fail;
}
@ -1379,32 +1379,32 @@ static int setup_pam(
if (r < 0)
goto fail;
if (r > 0) {
pam_code = sym_pam_set_item(handle, PAM_TTY, tty);
pam_code = sym_pam_set_item(pamh, PAM_TTY, tty);
if (pam_code != PAM_SUCCESS)
goto fail;
}
STRV_FOREACH(nv, *env) {
pam_code = sym_pam_putenv(handle, *nv);
pam_code = sym_pam_putenv(pamh, *nv);
if (pam_code != PAM_SUCCESS)
goto fail;
}
pam_code = sym_pam_acct_mgmt(handle, flags);
pam_code = sym_pam_acct_mgmt(pamh, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
pam_code = sym_pam_setcred(handle, PAM_ESTABLISH_CRED | flags);
pam_code = sym_pam_setcred(pamh, PAM_ESTABLISH_CRED | flags);
if (pam_code != PAM_SUCCESS)
pam_syslog_pam_error(handle, LOG_DEBUG, pam_code, "pam_setcred(PAM_ESTABLISH_CRED) failed, ignoring: @PAMERR@");
pam_syslog_pam_error(pamh, LOG_DEBUG, pam_code, "pam_setcred(PAM_ESTABLISH_CRED) failed, ignoring: @PAMERR@");
pam_code = sym_pam_open_session(handle, flags);
pam_code = sym_pam_open_session(pamh, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
close_session = true;
e = sym_pam_getenvlist(handle);
e = sym_pam_getenvlist(pamh);
if (!e) {
pam_code = PAM_BUF_ERR;
goto fail;
@ -1479,7 +1479,7 @@ static int setup_pam(
/* If our parent died we'll end the session */
if (getppid() != parent_pid) {
pam_code = pam_close_session_and_delete_credentials(handle, flags);
pam_code = pam_close_session_and_delete_credentials(pamh, flags);
if (pam_code != PAM_SUCCESS)
goto child_finish;
}
@ -1489,7 +1489,7 @@ static int setup_pam(
child_finish:
/* NB: pam_end() when called in child processes should set PAM_DATA_SILENT to let the module
* know about this. See pam_end(3) */
(void) sym_pam_end(handle, pam_code | flags | PAM_DATA_SILENT);
(void) sym_pam_end(pamh, pam_code | flags | PAM_DATA_SILENT);
_exit(ret);
}
@ -1497,7 +1497,7 @@ static int setup_pam(
/* If the child was forked off successfully it will do all the cleanups, so forget about the handle
* here. */
handle = NULL;
pamh = NULL;
/* Unblock SIGTERM again in the parent */
assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0);
@ -1515,16 +1515,16 @@ static int setup_pam(
fail:
if (pam_code != PAM_SUCCESS) {
pam_syslog_pam_error(handle, LOG_ERR, pam_code, "PAM failed: @PAMERR@");
pam_syslog_pam_error(pamh, LOG_ERR, pam_code, "PAM failed: @PAMERR@");
r = -EPERM; /* PAM errors do not map to errno */
} else
log_error_errno(r, "PAM failed: %m");
if (handle) {
if (pamh) {
if (close_session)
pam_code = pam_close_session_and_delete_credentials(handle, flags);
pam_code = pam_close_session_and_delete_credentials(pamh, flags);
(void) sym_pam_end(handle, pam_code | flags);
(void) sym_pam_end(pamh, pam_code | flags);
}
closelog();

View File

@ -6,6 +6,5 @@ executables += [
'public' : true,
'conditions' : ['ENABLE_FIRSTBOOT'],
'sources' : files('firstboot.c'),
'dependencies' : libcrypt,
},
]

View File

@ -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,

File diff suppressed because it is too large Load Diff

13
src/include/musl/crypt.h Normal file
View File

@ -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

111
src/libc/musl/crypt.c Normal file
View File

@ -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;
}

View File

@ -5,6 +5,7 @@ if get_option('libc') != 'musl'
endif
libc_wrapper_sources += files(
'crypt.c',
'getopt.c',
'printf.c',
'stdio.c',

File diff suppressed because it is too large Load Diff

View File

@ -11,13 +11,13 @@
static const char DEFAULT_KEYNAME[] = "cryptsetup";
_public_ PAM_EXTERN int pam_sm_authenticate(
pam_handle_t *handle,
pam_handle_t *pamh,
int flags,
int argc, const char **argv) {
int r;
assert(handle);
assert(pamh);
r = dlopen_libpam();
if (r < 0)
@ -41,10 +41,10 @@ _public_ PAM_EXTERN int pam_sm_authenticate(
else if (streq(argv[i], "debug"))
debug = true;
else
pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]);
pam_syslog(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]);
}
pam_debug_syslog(handle, debug, "pam-systemd-loadkey: initializing...");
pam_debug_syslog(pamh, debug, "pam-systemd-loadkey: initializing...");
/* Retrieve the key. */
@ -52,13 +52,13 @@ _public_ PAM_EXTERN int pam_sm_authenticate(
serial = request_key("user", keyname, NULL, 0);
if (serial < 0) {
if (errno == ENOKEY) {
pam_debug_syslog(handle, debug, "Key not found: %s", keyname);
pam_debug_syslog(pamh, debug, "Key not found: %s", keyname);
return PAM_AUTHINFO_UNAVAIL;
} else if (errno == EKEYEXPIRED) {
pam_debug_syslog(handle, debug, "Key expired: %s", keyname);
pam_debug_syslog(pamh, debug, "Key expired: %s", keyname);
return PAM_AUTHINFO_UNAVAIL;
} else
return pam_syslog_errno(handle, LOG_ERR, errno, "Failed to look up the key: %m");
return pam_syslog_errno(pamh, LOG_ERR, errno, "Failed to look up the key: %m");
}
_cleanup_(erase_and_freep) void *p = NULL;
@ -66,30 +66,30 @@ _public_ PAM_EXTERN int pam_sm_authenticate(
r = keyring_read(serial, &p, &n);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to read the key: %m");
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to read the key: %m");
/* Split the key by NUL. Set the last item as authtok. */
_cleanup_strv_free_erase_ char **passwords = strv_parse_nulstr(p, n);
if (!passwords)
return pam_log_oom(handle);
return pam_log_oom(pamh);
size_t passwords_len = strv_length(passwords);
if (passwords_len == 0) {
pam_debug_syslog(handle, debug, "Key is empty.");
pam_debug_syslog(pamh, debug, "Key is empty.");
return PAM_AUTHINFO_UNAVAIL;
} else if (passwords_len > 1)
pam_debug_syslog(handle, debug, "Multiple passwords found in the key. Using the last one.");
pam_debug_syslog(pamh, debug, "Multiple passwords found in the key. Using the last one.");
r = pam_set_item(handle, PAM_AUTHTOK, passwords[passwords_len - 1]);
r = pam_set_item(pamh, PAM_AUTHTOK, passwords[passwords_len - 1]);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
return PAM_SUCCESS;
}
_public_ PAM_EXTERN int pam_sm_setcred(
pam_handle_t *handle,
pam_handle_t *pamh,
int flags,
int argc, const char **argv) {

View File

@ -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;
}

View File

@ -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);

View File

@ -367,7 +367,7 @@ libshared_deps = [threads,
libaudit_cflags,
libblkid_cflags,
libbpf_cflags,
libcrypt,
libcrypt_cflags,
libcryptsetup_cflags,
libdl,
libdw_cflags,

View File

@ -73,20 +73,20 @@ int errno_to_pam_error(int error) {
return ERRNO_VALUE(error) == ENOMEM ? PAM_BUF_ERR : PAM_SERVICE_ERR;
}
int pam_syslog_errno(pam_handle_t *handle, int level, int error, const char *format, ...) {
int pam_syslog_errno(pam_handle_t *pamh, int level, int error, const char *format, ...) {
va_list ap;
error = ERRNO_VALUE(error);
LOCAL_ERRNO(error);
va_start(ap, format);
sym_pam_vsyslog(handle, level, format, ap);
sym_pam_vsyslog(pamh, level, format, ap);
va_end(ap);
return errno_to_pam_error(error);
}
int pam_syslog_pam_error(pam_handle_t *handle, int level, int error, const char *format, ...) {
int pam_syslog_pam_error(pam_handle_t *pamh, int level, int error, const char *format, ...) {
/* This wraps pam_syslog() but will replace @PAMERR@ with a string from pam_strerror().
* @PAMERR@ must be at the very end. */
@ -95,7 +95,7 @@ int pam_syslog_pam_error(pam_handle_t *handle, int level, int error, const char
const char *p = endswith(format, "@PAMERR@");
if (p) {
const char *pamerr = sym_pam_strerror(handle, error);
const char *pamerr = sym_pam_strerror(pamh, error);
if (strchr(pamerr, '%'))
pamerr = "n/a"; /* We cannot have any formatting chars */
@ -103,10 +103,10 @@ int pam_syslog_pam_error(pam_handle_t *handle, int level, int error, const char
xsprintf(buf, "%.*s%s", (int)(p - format), format, pamerr);
DISABLE_WARNING_FORMAT_NONLITERAL;
sym_pam_vsyslog(handle, level, buf, ap);
sym_pam_vsyslog(pamh, level, buf, ap);
REENABLE_WARNING;
} else
sym_pam_vsyslog(handle, level, format, ap);
sym_pam_vsyslog(pamh, level, format, ap);
va_end(ap);
@ -142,7 +142,7 @@ static PamBusData *pam_bus_data_free(PamBusData *d) {
DEFINE_TRIVIAL_CLEANUP_FUNC(PamBusData*, pam_bus_data_free);
static void pam_bus_data_destroy(pam_handle_t *handle, void *data, int error_status) {
static void pam_bus_data_destroy(pam_handle_t *pamh, void *data, int error_status) {
/* Destructor when called from PAM. Note that error_status is supposed to tell us via PAM_DATA_SILENT
* whether we are called in a forked off child of the PAM session or in the original parent. We don't
* bother with that however, and instead rely on the PID checks that sd_bus_flush_close_unref() does
@ -156,7 +156,7 @@ static void pam_bus_data_destroy(pam_handle_t *handle, void *data, int error_sta
if (FLAGS_SET(error_status, PAM_DATA_SILENT) &&
d->bus && bus_origin_changed(d->bus))
/* Please adjust test/units/end.sh when updating the log message. */
sym_pam_syslog(handle, LOG_DEBUG,
sym_pam_syslog(pamh, LOG_DEBUG,
"Warning: cannot close sd-bus connection (%s) after fork when it was opened before the fork.",
strna(d->cache_id));
@ -180,7 +180,7 @@ static char* pam_make_bus_cache_id(const char *module_name) {
void pam_bus_data_disconnectp(PamBusData **_d) {
PamBusData *d = *ASSERT_PTR(_d);
pam_handle_t *handle;
pam_handle_t *pamh;
int r;
/* Disconnects the connection explicitly (for use via _cleanup_()) when called */
@ -188,17 +188,17 @@ void pam_bus_data_disconnectp(PamBusData **_d) {
if (!d)
return;
handle = ASSERT_PTR(d->pam_handle); /* Keep a reference to the session even after 'd' might be invalidated */
pamh = ASSERT_PTR(d->pam_handle); /* Keep a reference to the session even after 'd' might be invalidated */
r = sym_pam_set_data(handle, ASSERT_PTR(d->cache_id), NULL, NULL);
r = sym_pam_set_data(pamh, ASSERT_PTR(d->cache_id), NULL, NULL);
if (r != PAM_SUCCESS)
pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to release PAM user record data, ignoring: @PAMERR@");
pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to release PAM user record data, ignoring: @PAMERR@");
/* Note, the pam_set_data() call will invalidate 'd', don't access here anymore */
}
int pam_acquire_bus_connection(
pam_handle_t *handle,
pam_handle_t *pamh,
const char *module_name,
bool debug,
sd_bus **ret_bus,
@ -208,39 +208,39 @@ int pam_acquire_bus_connection(
_cleanup_free_ char *cache_id = NULL;
int r;
assert(handle);
assert(pamh);
assert(module_name);
assert(ret_bus);
cache_id = pam_make_bus_cache_id(module_name);
if (!cache_id)
return pam_log_oom(handle);
return pam_log_oom(pamh);
/* We cache the bus connection so that we can share it between the session and the authentication hooks */
r = sym_pam_get_data(handle, cache_id, (const void**) &d);
r = sym_pam_get_data(pamh, cache_id, (const void**) &d);
if (r == PAM_SUCCESS && d)
goto success;
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
d = new(PamBusData, 1);
if (!d)
return pam_log_oom(handle);
return pam_log_oom(pamh);
*d = (PamBusData) {
.cache_id = TAKE_PTR(cache_id),
.pam_handle = handle,
.pam_handle = pamh,
};
r = sd_bus_open_system(&d->bus);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to connect to system bus: %m");
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to connect to system bus: %m");
r = sym_pam_set_data(handle, d->cache_id, d, pam_bus_data_destroy);
r = sym_pam_set_data(pamh, d->cache_id, d, pam_bus_data_destroy);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM bus data: @PAMERR@");
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM bus data: @PAMERR@");
pam_debug_syslog(handle, debug, "New sd-bus connection (%s) opened.", d->cache_id);
pam_debug_syslog(pamh, debug, "New sd-bus connection (%s) opened.", d->cache_id);
success:
*ret_bus = sd_bus_ref(d->bus);
@ -253,38 +253,34 @@ success:
return PAM_SUCCESS;
}
int pam_get_bus_data(
pam_handle_t *handle,
const char *module_name,
PamBusData **ret) {
int pam_get_bus_data(pam_handle_t *pamh, const char *module_name, PamBusData **ret) {
PamBusData *d = NULL;
_cleanup_free_ char *cache_id = NULL;
int r;
assert(handle);
assert(pamh);
assert(module_name);
assert(ret);
cache_id = pam_make_bus_cache_id(module_name);
if (!cache_id)
return pam_log_oom(handle);
return pam_log_oom(pamh);
/* We cache the bus connection so that we can share it between the session and the authentication hooks */
r = sym_pam_get_data(handle, cache_id, (const void**) &d);
r = sym_pam_get_data(pamh, cache_id, (const void**) &d);
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
*ret = d;
return PAM_SUCCESS;
}
void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status) {
void pam_cleanup_free(pam_handle_t *pamh, void *data, int error_status) {
/* A generic destructor for pam_set_data() that just frees the specified data */
free(data);
}
void pam_cleanup_close(pam_handle_t *handle, void *data, int error_status) {
void pam_cleanup_close(pam_handle_t *pamh, void *data, int error_status) {
/* A generic destructor for pam_set_data() that just closes the specified fd.
*
@ -298,13 +294,13 @@ void pam_cleanup_close(pam_handle_t *handle, void *data, int error_status) {
safe_close(PTR_TO_FD(data));
}
int pam_get_item_many_internal(pam_handle_t *handle, ...) {
int pam_get_item_many_internal(pam_handle_t *pamh, ...) {
va_list ap;
int r;
assert(handle);
assert(pamh);
va_start(ap, handle);
va_start(ap, pamh);
for (;;) {
int item_type = va_arg(ap, int);
if (item_type <= 0) {
@ -313,7 +309,7 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
}
const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = sym_pam_get_item(handle, item_type, value);
r = sym_pam_get_item(pamh, item_type, value);
if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
break;
}
@ -322,13 +318,13 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
return r;
}
int pam_get_data_many_internal(pam_handle_t *handle, ...) {
int pam_get_data_many_internal(pam_handle_t *pamh, ...) {
va_list ap;
int r;
assert(handle);
assert(pamh);
va_start(ap, handle);
va_start(ap, pamh);
for (;;) {
const char *data_name = va_arg(ap, const char *);
if (!data_name) {
@ -337,7 +333,7 @@ int pam_get_data_many_internal(pam_handle_t *handle, ...) {
}
const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = sym_pam_get_data(handle, data_name, value);
r = sym_pam_get_data(pamh, data_name, value);
if (!IN_SET(r, PAM_NO_MODULE_DATA, PAM_SUCCESS))
break;
}
@ -346,11 +342,11 @@ int pam_get_data_many_internal(pam_handle_t *handle, ...) {
return r;
}
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) {
int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, const char *fmt, ...) {
va_list args;
int r;
assert(handle);
assert(pamh);
assert(fmt);
/* This is just like pam_prompt(), but does not noisily (i.e. beyond LOG_DEBUG) log on its own, but leaves that to the caller */
@ -363,11 +359,11 @@ int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, co
return PAM_BUF_ERR;
const struct pam_conv *conv = NULL;
r = sym_pam_get_item(handle, PAM_CONV, (const void**) &conv);
r = sym_pam_get_item(pamh, PAM_CONV, (const void**) &conv);
if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM))
return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Failed to get conversation function structure: @PAMERR@");
return pam_syslog_pam_error(pamh, LOG_DEBUG, r, "Failed to get conversation function structure: @PAMERR@");
if (!conv || !conv->conv) {
sym_pam_syslog(handle, LOG_DEBUG, "No conversation function.");
sym_pam_syslog(pamh, LOG_DEBUG, "No conversation function.");
return PAM_SYSTEM_ERR;
}
@ -380,7 +376,7 @@ int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, co
r = conv->conv(1, &pmessage, &response, conv->appdata_ptr);
_cleanup_(erase_and_freep) char *rr = response ? response->resp : NULL; /* make sure string is freed + erased */
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Conversation function failed: @PAMERR@");
return pam_syslog_pam_error(pamh, LOG_DEBUG, r, "Conversation function failed: @PAMERR@");
if (ret_response)
*ret_response = TAKE_PTR(rr);

View File

@ -33,9 +33,9 @@ void pam_log_setup(void);
int errno_to_pam_error(int error) _const_;
int pam_syslog_errno(pam_handle_t *handle, int level, int error, const char *format, ...) _printf_(4,5);
int pam_syslog_errno(pam_handle_t *pamh, int level, int error, const char *format, ...) _printf_(4,5);
int pam_syslog_pam_error(pam_handle_t *handle, int level, int error, const char *format, ...) _printf_(4,5);
int pam_syslog_pam_error(pam_handle_t *pamh, int level, int error, const char *format, ...) _printf_(4,5);
/* Call sym_pam_syslog if debug is enabled */
#define pam_debug_syslog(handle, debug, fmt, ...) \
@ -53,19 +53,19 @@ int pam_syslog_pam_error(pam_handle_t *handle, int level, int error, const char
errno_to_pam_error(_error); \
})
static inline int pam_log_oom(pam_handle_t *handle) {
static inline int pam_log_oom(pam_handle_t *pamh) {
/* This is like log_oom(), but uses PAM logging */
return pam_syslog_errno(handle, LOG_ERR, ENOMEM, "Out of memory.");
return pam_syslog_errno(pamh, LOG_ERR, ENOMEM, "Out of memory.");
}
static inline int pam_bus_log_create_error(pam_handle_t *handle, int r) {
static inline int pam_bus_log_create_error(pam_handle_t *pamh, int r) {
/* This is like bus_log_create_error(), but uses PAM logging */
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to create bus message: %m");
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to create bus message: %m");
}
static inline int pam_bus_log_parse_error(pam_handle_t *handle, int r) {
static inline int pam_bus_log_parse_error(pam_handle_t *pamh, int r) {
/* This is like bus_log_parse_error(), but uses PAM logging */
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to parse bus message: %m");
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to parse bus message: %m");
}
typedef struct PamBusData PamBusData;
@ -74,23 +74,23 @@ void pam_bus_data_disconnectp(PamBusData **d);
/* Use a different module name per different PAM module. They are all loaded in the same namespace, and this
* helps avoid a clash in the internal data structures of sd-bus. It will be used as key for cache items. */
int pam_acquire_bus_connection(
pam_handle_t *handle,
pam_handle_t *pamh,
const char *module_name,
bool debug,
sd_bus **ret_bus,
PamBusData **ret_pam_bus_data);
int pam_get_bus_data(pam_handle_t *handle, const char *module_name, PamBusData **ret);
int pam_get_bus_data(pam_handle_t *pamh, const char *module_name, PamBusData **ret);
void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
void pam_cleanup_close(pam_handle_t *handle, void *data, int error_status);
void pam_cleanup_free(pam_handle_t *pamh, void *data, int error_status);
void pam_cleanup_close(pam_handle_t *pamh, void *data, int error_status);
int pam_get_item_many_internal(pam_handle_t *handle, ...);
int pam_get_item_many_internal(pam_handle_t *pamh, ...);
#define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1)
int pam_get_data_many_internal(pam_handle_t *handle, ...) _sentinel_;
int pam_get_data_many_internal(pam_handle_t *pamh, ...) _sentinel_;
#define pam_get_data_many(handle, ...) pam_get_data_many_internal(handle, __VA_ARGS__, NULL)
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);
int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);
#else

View File

@ -345,7 +345,7 @@ executables += [
},
test_template + {
'sources' : files('test-libcrypt-util.c'),
'dependencies' : libcrypt,
'conditions' : ['HAVE_LIBCRYPT'],
'timeout' : 120,
},
test_template + {

View File

@ -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);

View File

@ -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;
}
static void test_hash_password_full(void) {
log_info("/* %s */", __func__);
_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);
ASSERT_OK(hash_password(i, &hashed));
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",
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) == true);
assert_se(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
i));
ASSERT_OK_POSITIVE(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",
i));
ASSERT_OK_ZERO(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... */
""));
ASSERT_OK_ZERO(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
hashed,
"$y$j9T$SAayASazWZIQeJd9AS02m/$"),
"") == false);
""));
}
}
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);

View File

@ -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()));

View File

@ -6,7 +6,7 @@
# E.g.
# tools/setup-musl-build.sh build-musl -Dbuildtype=debugoptimized && ninja -C build-musl
set -eu
set -eux
BUILD_DIR="${1:?}"
shift
@ -53,7 +53,6 @@ LINKS=(
security
selinux
sys/acl.h
sys/capability.h
tss2
xen
xkbcommon
@ -63,6 +62,7 @@ LINKS=(
zstd_errors.h
)
rm -rf "${SETUP_DIR}"
for t in "${LINKS[@]}"; do
[[ -e /usr/include/"$t" ]]
link="${SETUP_DIR}/usr/include/${t}"
@ -82,4 +82,4 @@ env \
CXX=musl-gcc \
CFLAGS="$CFLAGS" \
CXXFLAGS="$CFLAGS" \
meson setup -Ddbus-interfaces-dir=no -Dlibc=musl "${BUILD_DIR}" "${@}"
meson setup --reconfigure -Ddbus-interfaces-dir=no -Dlibc=musl "${BUILD_DIR}" "${@}"