Files
systemd/src/shared/libcrypt-util.c
T
Daan De Meyer d158595dc7 tree-wide: move static dl handles into their dlopen_*() functions
Each dlopen_*() wrapper kept its dl handle as a file-scope
'static void *xxx_dl = NULL;' even though only the wrapper itself
ever referenced it. Move each one inside the corresponding function
so its scope matches its actual use, leaving the rest of each
translation unit free of the unused file-scope name.

In pcre2-util.c the assert(pcre2_dl) in pattern_matches_and_log()
becomes assert(sym_pcre2_match), which carries the same invariant
(pattern_compile_and_log() ran dlopen_pcre2()).
2026-05-19 19:22:06 +00:00

171 lines
5.0 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#if HAVE_LIBCRYPT
# include <crypt.h>
#endif
#include "sd-dlopen.h"
#include "alloc-util.h"
#include "dlfcn-util.h"
#include "errno-util.h"
#include "libcrypt-util.h"
#include "log.h"
#include "string-util.h"
#include "strv.h"
#if HAVE_LIBCRYPT
#ifdef __GLIBC__
static DLSYM_PROTOTYPE(crypt_gensalt_ra) = NULL;
static DLSYM_PROTOTYPE(crypt_preferred_method) = NULL;
static DLSYM_PROTOTYPE(crypt_ra) = NULL;
#else
static DLSYM_PROTOTYPE(crypt_gensalt_ra) = missing_crypt_gensalt_ra;
static DLSYM_PROTOTYPE(crypt_preferred_method) = missing_crypt_preferred_method;
static DLSYM_PROTOTYPE(crypt_ra) = missing_crypt_ra;
#endif
int make_salt(char **ret) {
const char *e;
char *salt;
int r;
assert(ret);
r = dlopen_libcrypt(LOG_DEBUG);
if (r < 0)
return r;
e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
if (!e)
e = sym_crypt_preferred_method();
log_debug("Generating salt for hash prefix: %s", e);
salt = sym_crypt_gensalt_ra(e, 0, NULL, 0);
if (!salt)
return -errno;
*ret = salt;
return 0;
}
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;
assert(password);
assert(ret);
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);
if (!p)
return log_debug_errno(errno_or_else(SYNTHETIC_ERRNO(EINVAL)), "crypt_ra() failed: %m");
return strdup_to(ret, p);
}
int test_password_one(const char *hashed_password, const char *password) {
_cleanup_(erase_and_freep) void *cd_data = NULL;
int r, cd_size = 0;
const char *k;
assert(hashed_password);
assert(password);
r = dlopen_libcrypt(LOG_DEBUG);
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
* different meanings.
*
* In particular, we allow locked passwords, i.e. strings starting with "!", including just "!",
* i.e. the locked empty password. See also fc58c0c7bf7e4f525b916e3e5be0de2307fef04e.
*/
if (!s)
return false;
s += strspn(s, "!"); /* Skip (possibly duplicated) locking prefix */
return !STR_IN_SET(s, "x", "*");
}
int dlopen_libcrypt(int log_level) {
#if HAVE_LIBCRYPT
#ifdef __GLIBC__
static void *libcrypt_dl = NULL;
static int cached = 0;
int r = -ENOENT;
if (libcrypt_dl)
return 1; /* Already loaded */
if (cached < 0)
return cached; /* Already tried, and failed. */
/* Several distributions like Debian/Ubuntu and OpenSUSE provide libxcrypt as libcrypt.so.1
* (libcrypt.so.1.1 on some architectures), while others like Fedora/CentOS and Arch provide it as
* libcrypt.so.2. */
SD_ELF_NOTE_DLOPEN(
"crypt",
"Support for hashing passwords",
SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
"libcrypt.so.2", "libcrypt.so.1", "libcrypt.so.1.1");
FOREACH_STRING(soname, "libcrypt.so.2", "libcrypt.so.1", "libcrypt.so.1.1") {
r = dlopen_many_sym_or_warn(
&libcrypt_dl, soname, LOG_DEBUG,
DLSYM_ARG(crypt_gensalt_ra),
DLSYM_ARG(crypt_preferred_method),
DLSYM_ARG(crypt_ra));
if (r >= 0)
break;
}
if (r < 0)
return cached = log_full_errno(log_level, r, "Failed to load libcrypt: %m");
#endif
return 1;
#else
return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
"libcrypt support is not compiled in.");
#endif
}