1
0
mirror of https://github.com/systemd/systemd synced 2026-03-19 19:44:48 +01:00

Compare commits

..

No commits in common. "5fcca42f18e421867c6471e36cebf82daed33a89" and "431fc656bcff3038fa257d7dcaf577f02361760a" have entirely different histories.

23 changed files with 840 additions and 874 deletions

2
README
View File

@ -212,7 +212,7 @@ REQUIREMENTS:
newer though. TL;DR: turn audit off, still.
glibc >= 2.31
libxcrypt >= 4.4.0 (optional)
libxcrypt or glibc (<= 2.38 built with --enable-crypt)
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,10 +137,6 @@ 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,6 +685,15 @@ 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',
@ -1037,18 +1046,22 @@ else
libatomic = []
endif
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()
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')
endif
conf.set10('HAVE_LIBCRYPT', have)
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
bpf_framework = get_option('bpf-framework')
bpf_compiler = get_option('bpf-compiler')
@ -1581,11 +1594,10 @@ 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 : 'libcrypt, openssl, fdisk, and libcryptsetup required').allowed()
error_message : 'openssl, fdisk and libcryptsetup required').allowed()
conf.set10('ENABLE_HOMED', have)
have = have and conf.get('HAVE_PAM') == 1
@ -3112,7 +3124,6 @@ foreach tuple : [
['gnutls'],
['libarchive'],
['libbpf'],
['libcrypt'],
['libcryptsetup'],
['libcryptsetup-plugins'],
['libcurl'],

View File

@ -426,8 +426,6 @@ 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 *pamh, int flags) {
static int pam_close_session_and_delete_credentials(pam_handle_t *handle, int flags) {
int r, s;
assert(pamh);
assert(handle);
r = sym_pam_close_session(pamh, flags);
r = sym_pam_close_session(handle, flags);
if (r != PAM_SUCCESS)
pam_syslog_pam_error(pamh, LOG_DEBUG, r, "pam_close_session() failed: @PAMERR@");
pam_syslog_pam_error(handle, LOG_DEBUG, r, "pam_close_session() failed: @PAMERR@");
s = sym_pam_setcred(pamh, PAM_DELETE_CRED | flags);
s = sym_pam_setcred(handle, PAM_DELETE_CRED | flags);
if (s != PAM_SUCCESS)
pam_syslog_pam_error(pamh, LOG_DEBUG, r, "pam_setcred(PAM_DELETE_CRED) failed: @PAMERR@");
pam_syslog_pam_error(handle, 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 *pamh = NULL;
pam_handle_t *handle = 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, &pamh);
pam_code = sym_pam_start(context->pam_name, user, &conv, &handle);
if (pam_code != PAM_SUCCESS) {
pamh = NULL;
handle = 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(pamh, PAM_TTY, tty);
pam_code = sym_pam_set_item(handle, PAM_TTY, tty);
if (pam_code != PAM_SUCCESS)
goto fail;
}
STRV_FOREACH(nv, *env) {
pam_code = sym_pam_putenv(pamh, *nv);
pam_code = sym_pam_putenv(handle, *nv);
if (pam_code != PAM_SUCCESS)
goto fail;
}
pam_code = sym_pam_acct_mgmt(pamh, flags);
pam_code = sym_pam_acct_mgmt(handle, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
pam_code = sym_pam_setcred(pamh, PAM_ESTABLISH_CRED | flags);
pam_code = sym_pam_setcred(handle, PAM_ESTABLISH_CRED | flags);
if (pam_code != PAM_SUCCESS)
pam_syslog_pam_error(pamh, LOG_DEBUG, pam_code, "pam_setcred(PAM_ESTABLISH_CRED) failed, ignoring: @PAMERR@");
pam_syslog_pam_error(handle, LOG_DEBUG, pam_code, "pam_setcred(PAM_ESTABLISH_CRED) failed, ignoring: @PAMERR@");
pam_code = sym_pam_open_session(pamh, flags);
pam_code = sym_pam_open_session(handle, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
close_session = true;
e = sym_pam_getenvlist(pamh);
e = sym_pam_getenvlist(handle);
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(pamh, flags);
pam_code = pam_close_session_and_delete_credentials(handle, 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(pamh, pam_code | flags | PAM_DATA_SILENT);
(void) sym_pam_end(handle, 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. */
pamh = NULL;
handle = 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(pamh, LOG_ERR, pam_code, "PAM failed: @PAMERR@");
pam_syslog_pam_error(handle, 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 (pamh) {
if (handle) {
if (close_session)
pam_code = pam_close_session_and_delete_credentials(pamh, flags);
pam_code = pam_close_session_and_delete_credentials(handle, flags);
(void) sym_pam_end(pamh, pam_code | flags);
(void) sym_pam_end(handle, pam_code | flags);
}
closelog();

View File

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

View File

@ -64,6 +64,7 @@ executables += [
'sources' : systemd_homed_sources,
'extract' : systemd_homed_extract_sources,
'dependencies' : [
libcrypt,
libm,
libopenssl,
threads,
@ -79,6 +80,7 @@ executables += [
],
'dependencies' : [
libblkid_cflags,
libcrypt,
libfdisk,
libopenssl,
libp11kit_cflags,
@ -91,6 +93,7 @@ executables += [
'sources' : homectl_sources,
'objects' : ['systemd-homed'],
'dependencies' : [
libcrypt,
libdl,
libopenssl,
libp11kit_cflags,
@ -109,6 +112,7 @@ 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

View File

@ -1,13 +0,0 @@
/* 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

View File

@ -1,111 +0,0 @@
/* 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,7 +5,6 @@ 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 *pamh,
pam_handle_t *handle,
int flags,
int argc, const char **argv) {
int r;
assert(pamh);
assert(handle);
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(pamh, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]);
pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]);
}
pam_debug_syslog(pamh, debug, "pam-systemd-loadkey: initializing...");
pam_debug_syslog(handle, 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(pamh, debug, "Key not found: %s", keyname);
pam_debug_syslog(handle, debug, "Key not found: %s", keyname);
return PAM_AUTHINFO_UNAVAIL;
} else if (errno == EKEYEXPIRED) {
pam_debug_syslog(pamh, debug, "Key expired: %s", keyname);
pam_debug_syslog(handle, debug, "Key expired: %s", keyname);
return PAM_AUTHINFO_UNAVAIL;
} else
return pam_syslog_errno(pamh, LOG_ERR, errno, "Failed to look up the key: %m");
return pam_syslog_errno(handle, 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(pamh, LOG_ERR, r, "Failed to read the key: %m");
return pam_syslog_errno(handle, 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(pamh);
return pam_log_oom(handle);
size_t passwords_len = strv_length(passwords);
if (passwords_len == 0) {
pam_debug_syslog(pamh, debug, "Key is empty.");
pam_debug_syslog(handle, debug, "Key is empty.");
return PAM_AUTHINFO_UNAVAIL;
} else if (passwords_len > 1)
pam_debug_syslog(pamh, debug, "Multiple passwords found in the key. Using the last one.");
pam_debug_syslog(handle, debug, "Multiple passwords found in the key. Using the last one.");
r = pam_set_item(pamh, PAM_AUTHTOK, passwords[passwords_len - 1]);
r = pam_set_item(handle, PAM_AUTHTOK, passwords[passwords_len - 1]);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
return PAM_SUCCESS;
}
_public_ PAM_EXTERN int pam_sm_setcred(
pam_handle_t *pamh,
pam_handle_t *handle,
int flags,
int argc, const char **argv) {

View File

@ -1,160 +1,145 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#if HAVE_LIBCRYPT
# include <crypt.h>
#endif
#include <crypt.h>
#include <stdlib.h>
#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"
#if HAVE_LIBCRYPT
static void *libcrypt_dl = NULL;
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) {
#if HAVE_CRYPT_GENSALT_RA
const char *e;
char *salt;
int r;
assert(ret);
r = dlopen_libcrypt();
if (r < 0)
return 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. */
e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
if (!e)
e = sym_crypt_preferred_method();
#if HAVE_CRYPT_PREFERRED_METHOD
e = crypt_preferred_method();
#else
e = "$6$";
#endif
log_debug("Generating salt for hash prefix: %s", e);
salt = sym_crypt_gensalt_ra(e, 0, NULL, 0);
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
}
int hash_password(const char *password, char **ret) {
_cleanup_free_ char *salt = NULL;
_cleanup_(erase_and_freep) void *cd_data = NULL;
const char *p;
int r, cd_size = 0;
#if HAVE_CRYPT_RA
# define CRYPT_RA_NAME "crypt_ra"
#else
# define CRYPT_RA_NAME "crypt_r"
assert(password);
assert(ret);
/* 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;
const 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 = sym_crypt_ra(password, salt, &cd_data, &cd_size);
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() failed: %m");
return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)),
CRYPT_RA_NAME "() 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
@ -170,3 +155,34 @@ 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,21 +3,11 @@
#include "shared-forward.h"
#if HAVE_LIBCRYPT
int dlopen_libcrypt(void);
int make_salt(char **ret);
int hash_password(const char *password, 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 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_cflags,
libcrypt,
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 *pamh, int level, int error, const char *format, ...) {
int pam_syslog_errno(pam_handle_t *handle, int level, int error, const char *format, ...) {
va_list ap;
error = ERRNO_VALUE(error);
LOCAL_ERRNO(error);
va_start(ap, format);
sym_pam_vsyslog(pamh, level, format, ap);
sym_pam_vsyslog(handle, level, format, ap);
va_end(ap);
return errno_to_pam_error(error);
}
int pam_syslog_pam_error(pam_handle_t *pamh, int level, int error, const char *format, ...) {
int pam_syslog_pam_error(pam_handle_t *handle, 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 *pamh, int level, int error, const char *f
const char *p = endswith(format, "@PAMERR@");
if (p) {
const char *pamerr = sym_pam_strerror(pamh, error);
const char *pamerr = sym_pam_strerror(handle, 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 *pamh, int level, int error, const char *f
xsprintf(buf, "%.*s%s", (int)(p - format), format, pamerr);
DISABLE_WARNING_FORMAT_NONLITERAL;
sym_pam_vsyslog(pamh, level, buf, ap);
sym_pam_vsyslog(handle, level, buf, ap);
REENABLE_WARNING;
} else
sym_pam_vsyslog(pamh, level, format, ap);
sym_pam_vsyslog(handle, 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 *pamh, void *data, int error_status) {
static void pam_bus_data_destroy(pam_handle_t *handle, 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 *pamh, void *data, int error_statu
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(pamh, LOG_DEBUG,
sym_pam_syslog(handle, 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 *pamh;
pam_handle_t *handle;
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;
pamh = ASSERT_PTR(d->pam_handle); /* Keep a reference to the session even after 'd' might be invalidated */
handle = ASSERT_PTR(d->pam_handle); /* Keep a reference to the session even after 'd' might be invalidated */
r = sym_pam_set_data(pamh, ASSERT_PTR(d->cache_id), NULL, NULL);
r = sym_pam_set_data(handle, ASSERT_PTR(d->cache_id), NULL, NULL);
if (r != PAM_SUCCESS)
pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to release PAM user record data, ignoring: @PAMERR@");
pam_syslog_pam_error(handle, 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 *pamh,
pam_handle_t *handle,
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(pamh);
assert(handle);
assert(module_name);
assert(ret_bus);
cache_id = pam_make_bus_cache_id(module_name);
if (!cache_id)
return pam_log_oom(pamh);
return pam_log_oom(handle);
/* We cache the bus connection so that we can share it between the session and the authentication hooks */
r = sym_pam_get_data(pamh, cache_id, (const void**) &d);
r = sym_pam_get_data(handle, 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(pamh, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
d = new(PamBusData, 1);
if (!d)
return pam_log_oom(pamh);
return pam_log_oom(handle);
*d = (PamBusData) {
.cache_id = TAKE_PTR(cache_id),
.pam_handle = pamh,
.pam_handle = handle,
};
r = sd_bus_open_system(&d->bus);
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to connect to system bus: %m");
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to connect to system bus: %m");
r = sym_pam_set_data(pamh, d->cache_id, d, pam_bus_data_destroy);
r = sym_pam_set_data(handle, d->cache_id, d, pam_bus_data_destroy);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to set PAM bus data: @PAMERR@");
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM bus data: @PAMERR@");
pam_debug_syslog(pamh, debug, "New sd-bus connection (%s) opened.", d->cache_id);
pam_debug_syslog(handle, debug, "New sd-bus connection (%s) opened.", d->cache_id);
success:
*ret_bus = sd_bus_ref(d->bus);
@ -253,34 +253,38 @@ success:
return PAM_SUCCESS;
}
int pam_get_bus_data(pam_handle_t *pamh, const char *module_name, PamBusData **ret) {
int pam_get_bus_data(
pam_handle_t *handle,
const char *module_name,
PamBusData **ret) {
PamBusData *d = NULL;
_cleanup_free_ char *cache_id = NULL;
int r;
assert(pamh);
assert(handle);
assert(module_name);
assert(ret);
cache_id = pam_make_bus_cache_id(module_name);
if (!cache_id)
return pam_log_oom(pamh);
return pam_log_oom(handle);
/* We cache the bus connection so that we can share it between the session and the authentication hooks */
r = sym_pam_get_data(pamh, cache_id, (const void**) &d);
r = sym_pam_get_data(handle, cache_id, (const void**) &d);
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
return pam_syslog_pam_error(pamh, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get bus connection: @PAMERR@");
*ret = d;
return PAM_SUCCESS;
}
void pam_cleanup_free(pam_handle_t *pamh, void *data, int error_status) {
void pam_cleanup_free(pam_handle_t *handle, 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 *pamh, void *data, int error_status) {
void pam_cleanup_close(pam_handle_t *handle, void *data, int error_status) {
/* A generic destructor for pam_set_data() that just closes the specified fd.
*
@ -294,13 +298,13 @@ void pam_cleanup_close(pam_handle_t *pamh, void *data, int error_status) {
safe_close(PTR_TO_FD(data));
}
int pam_get_item_many_internal(pam_handle_t *pamh, ...) {
int pam_get_item_many_internal(pam_handle_t *handle, ...) {
va_list ap;
int r;
assert(pamh);
assert(handle);
va_start(ap, pamh);
va_start(ap, handle);
for (;;) {
int item_type = va_arg(ap, int);
if (item_type <= 0) {
@ -309,7 +313,7 @@ int pam_get_item_many_internal(pam_handle_t *pamh, ...) {
}
const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = sym_pam_get_item(pamh, item_type, value);
r = sym_pam_get_item(handle, item_type, value);
if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
break;
}
@ -318,13 +322,13 @@ int pam_get_item_many_internal(pam_handle_t *pamh, ...) {
return r;
}
int pam_get_data_many_internal(pam_handle_t *pamh, ...) {
int pam_get_data_many_internal(pam_handle_t *handle, ...) {
va_list ap;
int r;
assert(pamh);
assert(handle);
va_start(ap, pamh);
va_start(ap, handle);
for (;;) {
const char *data_name = va_arg(ap, const char *);
if (!data_name) {
@ -333,7 +337,7 @@ int pam_get_data_many_internal(pam_handle_t *pamh, ...) {
}
const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = sym_pam_get_data(pamh, data_name, value);
r = sym_pam_get_data(handle, data_name, value);
if (!IN_SET(r, PAM_NO_MODULE_DATA, PAM_SUCCESS))
break;
}
@ -342,11 +346,11 @@ int pam_get_data_many_internal(pam_handle_t *pamh, ...) {
return r;
}
int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, const char *fmt, ...) {
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) {
va_list args;
int r;
assert(pamh);
assert(handle);
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 */
@ -359,11 +363,11 @@ int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, cons
return PAM_BUF_ERR;
const struct pam_conv *conv = NULL;
r = sym_pam_get_item(pamh, PAM_CONV, (const void**) &conv);
r = sym_pam_get_item(handle, PAM_CONV, (const void**) &conv);
if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM))
return pam_syslog_pam_error(pamh, LOG_DEBUG, r, "Failed to get conversation function structure: @PAMERR@");
return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Failed to get conversation function structure: @PAMERR@");
if (!conv || !conv->conv) {
sym_pam_syslog(pamh, LOG_DEBUG, "No conversation function.");
sym_pam_syslog(handle, LOG_DEBUG, "No conversation function.");
return PAM_SYSTEM_ERR;
}
@ -376,7 +380,7 @@ int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, cons
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(pamh, LOG_DEBUG, r, "Conversation function failed: @PAMERR@");
return pam_syslog_pam_error(handle, 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 *pamh, int level, int error, const char *format, ...) _printf_(4,5);
int pam_syslog_errno(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);
int pam_syslog_pam_error(pam_handle_t *handle, 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 *pamh, int level, int error, const char *f
errno_to_pam_error(_error); \
})
static inline int pam_log_oom(pam_handle_t *pamh) {
static inline int pam_log_oom(pam_handle_t *handle) {
/* This is like log_oom(), but uses PAM logging */
return pam_syslog_errno(pamh, LOG_ERR, ENOMEM, "Out of memory.");
return pam_syslog_errno(handle, LOG_ERR, ENOMEM, "Out of memory.");
}
static inline int pam_bus_log_create_error(pam_handle_t *pamh, int r) {
static inline int pam_bus_log_create_error(pam_handle_t *handle, int r) {
/* This is like bus_log_create_error(), but uses PAM logging */
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to create bus message: %m");
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to create bus message: %m");
}
static inline int pam_bus_log_parse_error(pam_handle_t *pamh, int r) {
static inline int pam_bus_log_parse_error(pam_handle_t *handle, int r) {
/* This is like bus_log_parse_error(), but uses PAM logging */
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to parse bus message: %m");
return pam_syslog_errno(handle, 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 *pamh,
pam_handle_t *handle,
const char *module_name,
bool debug,
sd_bus **ret_bus,
PamBusData **ret_pam_bus_data);
int pam_get_bus_data(pam_handle_t *pamh, const char *module_name, PamBusData **ret);
int pam_get_bus_data(pam_handle_t *handle, const char *module_name, PamBusData **ret);
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);
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);
int pam_get_item_many_internal(pam_handle_t *pamh, ...);
int pam_get_item_many_internal(pam_handle_t *handle, ...);
#define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1)
int pam_get_data_many_internal(pam_handle_t *pamh, ...) _sentinel_;
int pam_get_data_many_internal(pam_handle_t *handle, ...) _sentinel_;
#define pam_get_data_many(handle, ...) pam_get_data_many_internal(handle, __VA_ARGS__, NULL)
int pam_prompt_graceful(pam_handle_t *pamh, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);
int pam_prompt_graceful(pam_handle_t *handle, 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'),
'conditions' : ['HAVE_LIBCRYPT'],
'dependencies' : libcrypt,
'timeout' : 120,
},
test_template + {

View File

@ -11,7 +11,6 @@
#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"
@ -53,7 +52,6 @@ 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,25 +1,41 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <crypt.h>
#include "libcrypt-util.h"
#include "strv.h"
#include "tests.h"
TEST(make_salt) {
_cleanup_strv_free_ char **l = NULL;
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__);
for (int i = 0; i < 10; i++) {
_cleanup_free_ char *t;
ASSERT_OK(make_salt(&t));
assert_se(make_salt(&t) == 0);
log_info("%s", t);
ASSERT_FALSE(strv_contains(l, t));
ASSERT_OK(strv_consume(&l, TAKE_PTR(t)));
}
}
TEST(hash_password) {
static int test_hash_password(void) {
log_info("/* %s */", __func__);
/* As a warm-up exercise, check if we can hash passwords. */
bool have_sane_hash = false;
FOREACH_STRING(hash,
"ew3bU1.hoKk4o",
"$1$gc5rWpTB$wK1aul1PyBn9AX1z93stk1",
@ -27,46 +43,80 @@ TEST(hash_password) {
"$5$lGhDrcrao9zb5oIK$05KlOVG3ocknx/ThreqXE/gk.XzFFBMTksc4t2CPDUD",
"$6$c7wB/3GiRk0VHf7e$zXJ7hN0aLZapE.iO4mn/oHu6.prsXTUG/5k1AxpgR85ELolyAcaIGRgzfwJs3isTChMDBjnthZyaMCfCNxo9I.",
"$y$j9T$$9cKOWsAm4m97WiYk61lPPibZpy3oaGPIbsL4koRe/XD") {
int b;
#ifndef __GLIBC__
/* musl does not support yescrypt. */
if (hash[1] == 'y') {
ASSERT_OK_ZERO(test_password_one(hash, "ppp"));
continue;
}
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);
#endif
ASSERT_OK_POSITIVE(test_password_one(hash, "ppp"));
if (b && IN_SET(hash[1], '6', 'y'))
have_sane_hash = true;
}
FOREACH_STRING(i, "abc123", "h⸿sło") {
_cleanup_free_ char *hashed;
return have_sane_hash;
}
ASSERT_OK(hash_password(i, &hashed));
log_debug("\"%s\"\"%s\"", i, hashed);
static void test_hash_password_full(void) {
log_info("/* %s */", __func__);
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/$"),
""));
_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);
}
}
DEFINE_TEST_MAIN(LOG_DEBUG);
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;
}

View File

@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "format-util.h"
#include "libcrypt-util.h"
#include "log.h"
#include "memory-util.h"
#include "path-util.h"
@ -341,6 +342,18 @@ 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 -eux
set -eu
BUILD_DIR="${1:?}"
shift
@ -53,6 +53,7 @@ LINKS=(
security
selinux
sys/acl.h
sys/capability.h
tss2
xen
xkbcommon
@ -62,7 +63,6 @@ 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 --reconfigure -Ddbus-interfaces-dir=no -Dlibc=musl "${BUILD_DIR}" "${@}"
meson setup -Ddbus-interfaces-dir=no -Dlibc=musl "${BUILD_DIR}" "${@}"