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.
5fcca42f18
...
431fc656bc
2
README
2
README
@ -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
4
TODO
@ -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
|
||||
|
||||
39
meson.build
39
meson.build
@ -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'],
|
||||
|
||||
@ -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' },
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -6,5 +6,6 @@ executables += [
|
||||
'public' : true,
|
||||
'conditions' : ['ENABLE_FIRSTBOOT'],
|
||||
'sources' : files('firstboot.c'),
|
||||
'dependencies' : libcrypt,
|
||||
},
|
||||
]
|
||||
|
||||
@ -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
@ -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
|
||||
@ -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;
|
||||
}
|
||||
@ -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
@ -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) {
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -367,7 +367,7 @@ libshared_deps = [threads,
|
||||
libaudit_cflags,
|
||||
libblkid_cflags,
|
||||
libbpf_cflags,
|
||||
libcrypt_cflags,
|
||||
libcrypt,
|
||||
libcryptsetup_cflags,
|
||||
libdl,
|
||||
libdw_cflags,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -345,7 +345,7 @@ executables += [
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-libcrypt-util.c'),
|
||||
'conditions' : ['HAVE_LIBCRYPT'],
|
||||
'dependencies' : libcrypt,
|
||||
'timeout' : 120,
|
||||
},
|
||||
test_template + {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ASSERT_OK(hash_password(i, &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_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",
|
||||
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));
|
||||
ASSERT_OK_POSITIVE(test_password_many(STRV_MAKE("$W$j9T$dlCXwkX0GC5L6B8Gf.4PN/$VCyEH", /* no such method exists... */
|
||||
i) == true);
|
||||
assert_se(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",
|
||||
i) == true);
|
||||
assert_se(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... */
|
||||
"") == 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;
|
||||
}
|
||||
|
||||
@ -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()));
|
||||
|
||||
@ -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}" "${@}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user