Compare commits

...

19 Commits

Author SHA1 Message Date
Lennart Poettering caf6bd166f homed: default to "btrfs" as fs type in the LUKS backend
Apparently both Fedora and suse default to btrfs now, it should hence be
good enough for us too.

This enables a bunch of really nice things for us, most importanly we
can resize home directories freely (i.e. both grow *and* shrink) while
online. It also allows us to add nice subvolume based home directory
snapshotting later on.

Also, whenever we mention the three supported types, alaways mention
them in alphabetical order, which is also our new order of preference.
2020-08-19 15:46:07 +02:00
Lennart Poettering 1b13600948
Merge pull request #16771 from poettering/dyn-pwq
make libpwquality a dlopen() dependency + use it in systemd-firstboot, too
2020-08-19 15:40:41 +02:00
Zbigniew Jędrzejewski-Szmek 843ce053b4
Merge pull request #16762 from poettering/homed-fixlets
homed: five fixlets
2020-08-19 11:43:49 +02:00
Lennart Poettering b0073a017b
Merge pull request #16640 from keszybz/various-patches
Improve systemd-analyze security a bit and other assorted bits
2020-08-19 10:30:45 +02:00
Lennart Poettering 4d89db12fc update TODO 2020-08-19 10:04:08 +02:00
Lennart Poettering 7baf10a7be firstboot: hook up with libpwquality 2020-08-19 10:03:56 +02:00
Lennart Poettering 679badd7ba home: make libpwquality dep a runtime dlopen() one
Also, let's move the glue for this to src/shared/ so that we later can
reuse this in sysemd-firstboot.

Given that libpwquality is a more a leaf dependency, let's make it
runtime optional, so that downstream distros can downgrade their package
deps from Required to Recommended.
2020-08-19 10:03:24 +02:00
Lennart Poettering 64dc138d1e homework: downgrade chattr failure log message
NOCOW is a btrfs-only thing hence don't log louder than necessary if we
don't have it.
2020-08-18 12:41:31 +02:00
Lennart Poettering df14bda2b5 homework: explicitly close cryptsetup context, to not keep loopback device busy
The cryptsetup context pins the loop device even after deactivation.
Let's explicitly release the context to make sure the subsequent
loopback device detaching works cleanly.
2020-08-18 12:41:26 +02:00
Lennart Poettering 6d7b47eb3e homework: correct error passed into log message 2020-08-18 12:41:22 +02:00
Lennart Poettering a2a8a509cd homework: sync everything to disk before we rename LUKS loopback file into place
This how this works on Linux: when atomically creating a file we need to
fully populate it under a temporary name and then when we are fully
done, sync it and the directory it is contained in, before renaming it
to the final name.
2020-08-18 12:41:18 +02:00
Lennart Poettering 5e5e11b874 homed: downgrade quota message in containers
quota syscalls and operations are typically prohibited in containers.
Let's not make noise about that, needlessly.
2020-08-18 12:40:57 +02:00
Zbigniew Jędrzejewski-Szmek 3a193ac62e shared/cgroup-setup: reduce scope of variables 2020-08-17 19:48:32 +02:00
Zbigniew Jędrzejewski-Szmek 618727dae5 basic/utf8: rename parameter
Every time I was using this function I had to check whether "newline"
means that newlines are good or bad.
2020-08-17 19:48:32 +02:00
Zbigniew Jędrzejewski-Szmek 9a485918db basic/string-util: reduce scope of variables 2020-08-17 19:48:32 +02:00
Zbigniew Jędrzejewski-Szmek b91ae210e6 journal: adjust line about when the journal begins and ends
This comes up occasionally with new users. The phrase "Logs begin ..." is
ambiguous because it can be taken to mean the logs being displayed or all logs
(the intended meaning). Let's rephrase this as "Journal begins ..." to make
this clearer.
2020-08-17 19:48:32 +02:00
Zbigniew Jędrzejewski-Szmek a9134af2e3 analyze-security: include an actual syscall name in the message
This information was already available in the debug output, but I think it
is good to include it in the message in the table. This makes it easier to wrap
one's head around the allowlist/denylist filtering.
2020-08-17 19:48:32 +02:00
Zbigniew Jędrzejewski-Szmek 01ecb3674a analyze-security: do not assign badness to filtered-out syscalls
Fixes #16451, https://bugzilla.redhat.com/show_bug.cgi?id=1856273.
2020-08-01 11:54:26 +02:00
Zbigniew Jędrzejewski-Szmek c2cfb12641 NEWS: clarify two points
I was reading a summary of changes on Phoronix, and (while not incorrect)
those two points were rather misleading.
2020-08-01 11:54:26 +02:00
29 changed files with 437 additions and 263 deletions

21
NEWS
View File

@ -114,7 +114,9 @@ CHANGES WITH 246:
* tmpfs mounts automatically created by systemd (/tmp, /run, /dev/shm,
and others) now have a size and inode limits applied (50% of RAM for
/tmp and /dev/shm, 10% of RAM for other mounts, etc.)
/tmp and /dev/shm, 10% of RAM for other mounts, etc.). Please note
that the implicit kernel default is 50% too, so there is no change
in the size limit for /tmp and /dev/shm.
* nss-mymachines lost support for resolution of users and groups, and
now only does resolution of hostnames. This functionality is now
@ -156,12 +158,17 @@ CHANGES WITH 246:
now automatically set to "Y" at boot, in order to enable pstore
generation for collection with systemd-pstore.
* A new 'hwdb' file has been added that collects information about PCI
and USB devices that correctly support auto-suspend, on top of the
databases for this we import from the ChromiumOS project. If you have
a device that supports auto-suspend correctly and where it should be
enabled by default, please submit a patch that adds it to the
database (see /usr/lib/udev/hwdb.d/60-autosuspend.hwdb).
* We provide a set of udev rules to enable auto-suspend on PCI and USB
devices that were tested to currectly support it. Previously, this
was distributed as a set of udev rules, but has now been replaced by
by a set of hwdb entries (and a much shorter udev rule to take action
if the device modalias matches one of the new hwdb entries).
As before, entries are periodically imported from the database
maintained by the ChromiumOS project. If you have a device that
supports auto-suspend correctly and where it should be enabled by
default, please submit a patch that adds it to the database (see
/usr/lib/udev/hwdb.d/60-autosuspend.hwdb).
* systemd-udevd gained the new configuration option timeout_signal= as well
as a corresponding kernel command line option udev.timeout_signal=.

4
TODO
View File

@ -87,8 +87,7 @@ Features:
* make us use dynamically fewer deps for containers in general purpose distros:
o turn into dlopen() deps:
- libpwquality (always) - only relevant for homed, and maybe soon
firstboot
- libidn2 (always)
- elfutils (always)
- p11-kit-trust (always)
- kmod-libs (only when called from PID 1)
@ -365,7 +364,6 @@ Features:
- fingerprint authentication, pattern authentication, …
- make sure "classic" user records can also be managed by homed
- make size of $XDG_RUNTIME_DIR configurable in user record
- reuse pwquality magic in firstboot
- query password from kernel keyring first
- update even if record is "absent"
- add a "access mode" + "fstype" field to the "status" section of json identity records reflecting the actually used access mode and fstype, even on non-luks backends

View File

@ -566,8 +566,8 @@
<term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
<listitem><para>When LUKS2 storage is used configures the file system type to use inside the home
directory LUKS2 container. One of <literal>ext4</literal>, <literal>xfs</literal>,
<literal>btrfs</literal>. If not specified
directory LUKS2 container. One of <literal>btrfs</literal>, <literal>ext4</literal>,
<literal>xfs</literal>. If not specified
<citerefentry><refentrytitle>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
defines which default file system type to use. Note that <literal>xfs</literal> is not recommended as
its support for file system resizing is too limited.</para></listitem>

View File

@ -63,9 +63,9 @@
<varlistentry>
<term><varname>DefaultFileSystemType=</varname></term>
<listitem><para>When using <literal>luks</literal> as storage (see above), selects the default file
system to use inside the user's LUKS volume. Takes one of <literal>ext4</literal>,
<literal>xfs</literal> or <literal>btrfs</literal>. If not specified defaults to
<literal>ext4</literal>. This setting has no effect if a different storage mechanism is used. The
system to use inside the user's LUKS volume. Takes one of <literal>btrfs</literal>,
<literal>ext4</literal> or <literal>xfs</literal>. If not specified defaults to
<literal>btrfs</literal>. This setting has no effect if a different storage mechanism is used. The
file system type selected on the <command>homectl</command> command line always takes
precedence.</para></listitem>
</varlistentry>

View File

@ -453,7 +453,7 @@
<term><option>--quiet</option></term>
<listitem><para>Suppresses all informational messages
(i.e. "-- Logs begin at …", "-- Reboot --"),
(i.e. "-- Journal begins at …", "-- Reboot --"),
any warning messages regarding
inaccessible system journals when run as a normal
user.</para></listitem>

View File

@ -463,11 +463,11 @@ Mon Dec 8 20:44:24 KST 2014
Running as unit: run-71.timer
Will run service as unit: run-71.service
# journalctl -b -u run-71.timer
-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
-- Journal begins at Fri 2014-12-05 19:09:21 KST, ends at Mon 2014-12-08 20:44:54 KST. --
Dec 08 20:44:38 container systemd[1]: Starting /bin/touch /tmp/foo.
Dec 08 20:44:38 container systemd[1]: Started /bin/touch /tmp/foo.
# journalctl -b -u run-71.service
-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
-- Journal begins at Fri 2014-12-05 19:09:21 KST, ends at Mon 2014-12-08 20:44:54 KST. --
Dec 08 20:44:48 container systemd[1]: Starting /bin/touch /tmp/foo...
Dec 08 20:44:48 container systemd[1]: Started /bin/touch /tmp/foo.</programlisting>
</example>

View File

@ -2174,8 +2174,7 @@ if conf.get('ENABLE_HOMED') == 1
link_with : [libshared],
dependencies : [threads,
libcrypt,
libopenssl,
libpwquality],
libopenssl],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
@ -2189,8 +2188,7 @@ if conf.get('ENABLE_HOMED') == 1
libcrypt,
libopenssl,
libp11kit,
libfido2,
libpwquality],
libfido2],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootbindir)

View File

@ -139,7 +139,7 @@ _homectl() {
comps=$(cat /etc/shells)
;;
--fs-type)
comps='ext4 xfs btrsf'
comps='btrfs ext4 xfs'
;;
--cifs-user-name)
comps=$(compgen -A user -- "$cur" )

View File

@ -493,7 +493,7 @@ static int assess_system_call_architectures(
#if HAVE_SECCOMP
static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f) {
static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
const char *syscall;
NULSTR_FOREACH(syscall, f->value) {
@ -503,7 +503,7 @@ static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilter
const SyscallFilterSet *g;
assert_se(g = syscall_filter_set_find(syscall));
if (syscall_names_in_filter(s, allow_list, g))
if (syscall_names_in_filter(s, allow_list, g, ret_offending_syscall))
return true; /* bad! */
continue;
@ -516,10 +516,13 @@ static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilter
if (set_contains(s, syscall) == allow_list) {
log_debug("Offending syscall filter item: %s", syscall);
if (ret_offending_syscall)
*ret_offending_syscall = syscall;
return true; /* bad! */
}
}
*ret_offending_syscall = NULL;
return false;
}
@ -530,43 +533,49 @@ static int assess_system_call_filter(
uint64_t *ret_badness,
char **ret_description) {
const SyscallFilterSet *f;
char *d = NULL;
uint64_t b;
assert(a);
assert(info);
assert(ret_badness);
assert(ret_description);
assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
f = syscall_filter_sets + a->parameter;
const SyscallFilterSet *f = syscall_filter_sets + a->parameter;
char *d = NULL;
uint64_t b;
if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
d = strdup("Service does not filter system calls");
b = 10;
} else {
bool bad;
const char *offender = NULL;
log_debug("Analyzing system call filter, checking against: %s", f->name);
bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f);
bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f, &offender);
log_debug("Result: %s", bad ? "bad" : "good");
if (info->system_call_filter_allow_list) {
if (bad) {
(void) asprintf(&d, "System call allow list defined for service, and %s is included", f->name);
(void) asprintf(&d, "System call allow list defined for service, and %s is included "
"(e.g. %s is allowed)",
f->name, offender);
b = 9;
} else {
(void) asprintf(&d, "System call allow list defined for service, and %s is not included", f->name);
(void) asprintf(&d, "System call allow list defined for service, and %s is not included",
f->name);
b = 0;
}
} else {
if (bad) {
(void) asprintf(&d, "System call deny list defined for service, and %s is not included", f->name);
(void) asprintf(&d, "System call deny list defined for service, and %s is not included "
"(e.g. %s is allowed)",
f->name, offender);
b = 10;
} else {
(void) asprintf(&d, "System call deny list defined for service, and %s is included", f->name);
b = 5;
(void) asprintf(&d, "System call deny list defined for service, and %s is included",
f->name);
b = 0;
}
}
}

View File

@ -135,8 +135,8 @@ char *strjoin_real(const char *x, ...) _sentinel_;
({ \
const char *_appendees_[] = { a, __VA_ARGS__ }; \
char *_d_, *_p_; \
size_t _len_ = 0; \
size_t _i_; \
size_t _len_ = 0; \
size_t _i_; \
for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
_len_ += strlen(_appendees_[_i_]); \
_p_ = _d_ = newa(char, _len_ + 1); \
@ -152,7 +152,6 @@ char *delete_trailing_chars(char *s, const char *bad);
char *truncate_nl(char *s);
static inline char *skip_leading_chars(const char *s, const char *bad) {
if (!s)
return NULL;
@ -231,11 +230,9 @@ REENABLE_WARNING;
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
size_t n;
assert(token);
n = strlen(token);
size_t n = strlen(token);
if (sz < n)
return NULL;
@ -251,20 +248,17 @@ static inline void *memory_startswith(const void *p, size_t sz, const char *toke
* It works only for ASCII strings.
*/
static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) {
size_t n, i;
assert(token);
n = strlen(token);
size_t n = strlen(token);
if (sz < n)
return NULL;
assert(p);
for (i = 0; i < n; i++) {
for (size_t i = 0; i < n; i++)
if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i]))
return NULL;
}
return (uint8_t*) p + n;
}

View File

@ -123,7 +123,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
return 0;
}
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
const char *p;
assert(str);
@ -140,7 +140,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
r = utf8_encoded_to_unichar(p, &val);
if (r < 0 ||
unichar_is_control(val) ||
(!newline && val == '\n'))
(!allow_newline && val == '\n'))
return false;
length -= encoded_len;

View File

@ -18,7 +18,7 @@ char *utf8_is_valid(const char *s) _pure_;
char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len);
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_;
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);

View File

@ -28,6 +28,7 @@
#include "path-util.h"
#include "pretty-print.h"
#include "proc-cmdline.h"
#include "pwquality-util.h"
#include "random-util.h"
#include "string-util.h"
#include "strv.h"
@ -568,8 +569,11 @@ static int prompt_root_password(void) {
msg1 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip):");
msg2 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter new root password again:");
suggest_passwords();
for (;;) {
_cleanup_strv_free_erase_ char **a = NULL, **b = NULL;
_cleanup_free_ char *error = NULL;
r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a);
if (r < 0)
@ -583,6 +587,12 @@ static int prompt_root_password(void) {
break;
}
r = quality_check_password(*a, "root", &error);
if (r < 0)
return log_error_errno(r, "Failed to check quality of password: %m");
if (r == 0)
log_warning("Password is weak, accepting anyway: %s", error);
r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b);
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");

View File

@ -30,6 +30,7 @@
#include "rlimit-util.h"
#include "spawn-polkit-agent.h"
#include "terminal-util.h"
#include "user-record-pwquality.h"
#include "user-record-show.h"
#include "user-record-util.h"
#include "user-record.h"
@ -1097,7 +1098,7 @@ static int create_home(int argc, char *argv[], void *userdata) {
/* If password quality enforcement is disabled, let's at least warn client side */
r = quality_check_password(hr, hr, &error);
r = user_record_quality_check_password(hr, hr, &error);
if (r < 0)
log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
}
@ -1955,7 +1956,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --image-path=PATH Path to image file/directory\n"
"\n%4$sLUKS Storage User Record Properties:%5$s\n"
" --fs-type=TYPE File system type to use in case of luks\n"
" storage (ext4, xfs, btrfs)\n"
" storage (btrfs, ext4, xfs)\n"
" --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
" when activated (mounted)\n"
" --luks-offline-discard=BOOL\n"

View File

@ -33,6 +33,7 @@
#include "strv.h"
#include "user-record-sign.h"
#include "user-record-util.h"
#include "user-record-pwquality.h"
#include "user-record.h"
#include "user-util.h"
@ -1289,7 +1290,7 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
if (h->record->enforce_password_policy == false)
log_debug("Password quality check turned off for account, skipping.");
else {
r = quality_check_password(h->record, secret, error);
r = user_record_quality_check_password(h->record, secret, error);
if (r < 0)
return r;
}
@ -1640,7 +1641,7 @@ int home_passwd(Home *h,
if (c->enforce_password_policy == false)
log_debug("Password quality check turned off for account, skipping.");
else {
r = quality_check_password(c, merged_secret, error);
r = user_record_quality_check_password(c, merged_secret, error);
if (r < 0)
return r;
}

View File

@ -512,6 +512,8 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
if (r < 0) {
if (ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
else if (ERRNO_IS_PRIVILEGE(r))
log_debug_errno(r, "UID quota support for %s prohibited, ignoring.", where);
else
log_warning_errno(r, "Failed to query quota on %s, ignoring: %m", where);

View File

@ -13,4 +13,4 @@
[Home]
#DefaultStorage=
#DefaultFileSystemType=ext4
#DefaultFileSystemType=btrfs

View File

@ -1110,7 +1110,9 @@ int home_prepare_luks(
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to fstat() image file: %m");
if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
return log_error_errno(errno, "Image file %s is not a regular file or block device: %m", ip);
return log_error_errno(
S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD),
"Image file %s is not a regular file or block device: %m", ip);
r = luks_validate(fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
if (r < 0)
@ -2015,7 +2017,8 @@ int home_create_luks(
r = chattr_fd(image_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", temporary_image_path);
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set file attributes on %s, ignoring: %m", temporary_image_path);
r = home_truncate(h, image_fd, temporary_image_path, host_size);
if (r < 0)
@ -2164,6 +2167,9 @@ int home_create_luks(
goto fail;
}
crypt_free(cd);
cd = NULL;
dm_activated = false;
loop = loop_device_unref(loop);
@ -2174,8 +2180,22 @@ int home_create_luks(
goto fail;
}
/* Sync everything to disk before we move things into place under the final name. */
if (fsync(image_fd) < 0) {
r = log_error_errno(r, "Failed to synchronize image to disk: %m");
goto fail;
}
if (disk_uuid_path)
(void) ioctl(image_fd, BLKRRPART, 0);
else {
/* If we operate on a file, sync the contaning directory too. */
r = fsync_directory_of_file(image_fd);
if (r < 0) {
log_error_errno(r, "Failed to synchronize directory of image file to disk: %m");
goto fail;
}
}
/* Let's close the image fd now. If we are operating on a real block device this will release the BSD
* lock that ensures udev doesn't interfere with what we are doing */

View File

@ -50,8 +50,8 @@ systemd_homed_sources = files('''
homed-varlink.c
homed-varlink.h
homed.c
pwquality-util.c
pwquality-util.h
user-record-pwquality.c
user-record-pwquality.h
user-record-sign.c
user-record-sign.h
user-record-util.c
@ -74,8 +74,8 @@ homectl_sources = files('''
homectl-pkcs11.c
homectl-pkcs11.h
homectl.c
pwquality-util.c
pwquality-util.h
user-record-pwquality.c
user-record-pwquality.h
user-record-util.c
user-record-util.h
'''.split())

View File

@ -1,186 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <unistd.h>
#if HAVE_PWQUALITY
/* pwquality.h uses size_t but doesn't include sys/types.h on its own */
#include <sys/types.h>
#include <pwquality.h>
#endif
#include "bus-common-errors.h"
#include "home-util.h"
#include "memory-util.h"
#include "pwquality-util.h"
#include "strv.h"
#if HAVE_PWQUALITY
DEFINE_TRIVIAL_CLEANUP_FUNC(pwquality_settings_t*, pwquality_free_settings);
static void pwquality_maybe_disable_dictionary(
pwquality_settings_t *pwq) {
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
const char *path;
int r;
r = pwquality_get_str_value(pwq, PWQ_SETTING_DICT_PATH, &path);
if (r < 0) {
log_warning("Failed to read libpwquality dictionary path, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
return;
}
// REMOVE THIS AS SOON AS https://github.com/libpwquality/libpwquality/pull/21 IS MERGED AND RELEASED
if (isempty(path))
path = "/usr/share/cracklib/pw_dict.pwd.gz";
if (isempty(path)) {
log_warning("Weird, no dictionary file configured, ignoring.");
return;
}
if (access(path, F_OK) >= 0)
return;
if (errno != ENOENT) {
log_warning_errno(errno, "Failed to check if dictionary file %s exists, ignoring: %m", path);
return;
}
r = pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, 0);
if (r < 0) {
log_warning("Failed to disable libpwquality dictionary check, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
return;
}
}
int quality_check_password(
UserRecord *hr,
UserRecord *secret,
sd_bus_error *error) {
_cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
void *auxerror;
int r;
assert(hr);
assert(secret);
pwq = pwquality_default_settings();
if (!pwq)
return log_oom();
r = pwquality_read_config(pwq, NULL, &auxerror);
if (r < 0)
log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuration, ignoring: %s",
pwquality_strerror(buf, sizeof(buf), r, auxerror));
pwquality_maybe_disable_dictionary(pwq);
/* This is a bit more complex than one might think at first. pwquality_check() would like to know the
* old password to make security checks. We support arbitrary numbers of passwords however, hence we
* call the function once for each combination of old and new password. */
/* Iterate through all new passwords */
STRV_FOREACH(pp, secret->password) {
bool called = false;
char **old;
r = test_password_many(hr->hashed_password, *pp);
if (r < 0)
return r;
if (r == 0) /* This is an old password as it isn't listed in the hashedPassword field, skip it */
continue;
/* Check this password against all old passwords */
STRV_FOREACH(old, secret->password) {
if (streq(*pp, *old))
continue;
r = test_password_many(hr->hashed_password, *old);
if (r < 0)
return r;
if (r > 0) /* This is a new password, not suitable as old password */
continue;
r = pwquality_check(pwq, *pp, *old, hr->user_name, &auxerror);
if (r < 0)
return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
pwquality_strerror(buf, sizeof(buf), r, auxerror));
called = true;
}
if (called)
continue;
/* If there are no old passwords, let's call pwquality_check() without any. */
r = pwquality_check(pwq, *pp, NULL, hr->user_name, &auxerror);
if (r < 0)
return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
pwquality_strerror(buf, sizeof(buf), r, auxerror));
}
return 0;
}
#define N_SUGGESTIONS 6
int suggest_passwords(void) {
_cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
_cleanup_strv_free_erase_ char **suggestions = NULL;
_cleanup_(erase_and_freep) char *joined = NULL;
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
void *auxerror;
size_t i;
int r;
pwq = pwquality_default_settings();
if (!pwq)
return log_oom();
r = pwquality_read_config(pwq, NULL, &auxerror);
if (r < 0)
log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuration, ignoring: %s",
pwquality_strerror(buf, sizeof(buf), r, auxerror));
pwquality_maybe_disable_dictionary(pwq);
suggestions = new0(char*, N_SUGGESTIONS+1);
if (!suggestions)
return log_oom();
for (i = 0; i < N_SUGGESTIONS; i++) {
r = pwquality_generate(pwq, 64, suggestions + i);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring: %s",
pwquality_strerror(buf, sizeof(buf), r, NULL));
}
joined = strv_join(suggestions, " ");
if (!joined)
return log_oom();
log_info("Password suggestions: %s", joined);
return 0;
}
#else
int quality_check_password(
UserRecord *hr,
UserRecord *secret,
sd_bus_error *error) {
assert(hr);
assert(secret);
return 0;
}
int suggest_passwords(void) {
return 0;
}
#endif

View File

@ -1,9 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
#include "user-record.h"
int quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error);
int suggest_passwords(void);

View File

@ -0,0 +1,90 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "bus-common-errors.h"
#include "errno-util.h"
#include "home-util.h"
#include "pwquality-util.h"
#include "strv.h"
#include "user-record-pwquality.h"
#include "user-record-util.h"
#if HAVE_PWQUALITY
int user_record_quality_check_password(
UserRecord *hr,
UserRecord *secret,
sd_bus_error *error) {
_cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
void *auxerror;
int r;
assert(hr);
assert(secret);
r = pwq_allocate_context(&pwq);
if (ERRNO_IS_NOT_SUPPORTED(r))
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to allocate libpwquality context: %m");
/* This is a bit more complex than one might think at first. pwquality_check() would like to know the
* old password to make security checks. We support arbitrary numbers of passwords however, hence we
* call the function once for each combination of old and new password. */
/* Iterate through all new passwords */
STRV_FOREACH(pp, secret->password) {
bool called = false;
char **old;
r = test_password_many(hr->hashed_password, *pp);
if (r < 0)
return r;
if (r == 0) /* This is an old password as it isn't listed in the hashedPassword field, skip it */
continue;
/* Check this password against all old passwords */
STRV_FOREACH(old, secret->password) {
if (streq(*pp, *old))
continue;
r = test_password_many(hr->hashed_password, *old);
if (r < 0)
return r;
if (r > 0) /* This is a new password, not suitable as old password */
continue;
r = sym_pwquality_check(pwq, *pp, *old, hr->user_name, &auxerror);
if (r < 0)
return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
called = true;
}
if (called)
continue;
/* If there are no old passwords, let's call pwquality_check() without any. */
r = sym_pwquality_check(pwq, *pp, NULL, hr->user_name, &auxerror);
if (r < 0)
return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
}
return 1;
}
#else
int user_record_quality_check_password(
UserRecord *hr,
UserRecord *secret,
sd_bus_error *error) {
return 0;
}
#endif

View File

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
#include "user-record.h"
int user_record_quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error);

View File

@ -2573,10 +2573,10 @@ int main(int argc, char *argv[]) {
if (r > 0) {
if (arg_follow)
printf("-- Logs begin at %s. --\n",
printf("-- Journal begins at %s. --\n",
format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start));
else
printf("-- Logs begin at %s, end at %s. --\n",
printf("-- Journal begins at %s, ends at %s. --\n",
format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start),
format_timestamp_maybe_utc(end_buf, sizeof(end_buf), end));
}

View File

@ -618,8 +618,6 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
}
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
CGroupController c;
CGroupMask done;
int r;
r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
@ -633,9 +631,9 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
return 0;
supported &= CGROUP_MASK_V1;
done = 0;
CGroupMask done = 0;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
for (CGroupController c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
const char *p = NULL;

View File

@ -191,6 +191,8 @@ shared_sources = files('''
pretty-print.h
ptyfwd.c
ptyfwd.h
pwquality-util.c
pwquality-util.h
reboot-util.c
reboot-util.h
resize-fs.c

191
src/shared/pwquality-util.c Normal file
View File

@ -0,0 +1,191 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <unistd.h>
#include "dlfcn-util.h"
#include "errno-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "pwquality-util.h"
#include "strv.h"
#if HAVE_PWQUALITY
static void *pwquality_dl = NULL;
int (*sym_pwquality_check)(pwquality_settings_t *pwq, const char *password, const char *oldpassword, const char *user, void **auxerror);
pwquality_settings_t *(*sym_pwquality_default_settings)(void);
void (*sym_pwquality_free_settings)(pwquality_settings_t *pwq);
int (*sym_pwquality_generate)(pwquality_settings_t *pwq, int entropy_bits, char **password);
int (*sym_pwquality_get_str_value)(pwquality_settings_t *pwq, int setting, const char **value);
int (*sym_pwquality_read_config)(pwquality_settings_t *pwq, const char *cfgfile, void **auxerror);
int (*sym_pwquality_set_int_value)(pwquality_settings_t *pwq, int setting, int value);
const char* (*sym_pwquality_strerror)(char *buf, size_t len, int errcode, void *auxerror);
int dlopen_pwquality(void) {
_cleanup_(dlclosep) void *dl = NULL;
int r;
if (pwquality_dl)
return 0; /* Already loaded */
dl = dlopen("libpwquality.so.1", RTLD_LAZY);
if (!dl)
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"libpwquality support is not installed: %s", dlerror());
r = dlsym_many_and_warn(
dl,
LOG_DEBUG,
&sym_pwquality_check, "pwquality_check",
&sym_pwquality_default_settings, "pwquality_default_settings",
&sym_pwquality_free_settings, "pwquality_free_settings",
&sym_pwquality_generate, "pwquality_generate",
&sym_pwquality_get_str_value, "pwquality_get_str_value",
&sym_pwquality_read_config, "pwquality_read_config",
&sym_pwquality_set_int_value, "pwquality_set_int_value",
&sym_pwquality_strerror, "pwquality_strerror",
NULL);
if (r < 0)
return r;
/* Note that we never release the reference here, because there's no real reason to, after all this
* was traditionally a regular shared library dependency which lives forever too. */
pwquality_dl = TAKE_PTR(dl);
return 1;
}
void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq) {
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
const char *path;
int r;
assert(pwq);
r = sym_pwquality_get_str_value(pwq, PWQ_SETTING_DICT_PATH, &path);
if (r < 0) {
log_debug("Failed to read libpwquality dictionary path, ignoring: %s",
sym_pwquality_strerror(buf, sizeof(buf), r, NULL));
return;
}
// REMOVE THIS AS SOON AS https://github.com/libpwquality/libpwquality/pull/21 IS MERGED AND RELEASED
if (isempty(path))
path = "/usr/share/cracklib/pw_dict.pwd.gz";
if (isempty(path)) {
log_debug("Weird, no dictionary file configured, ignoring.");
return;
}
if (access(path, F_OK) >= 0)
return;
if (errno != ENOENT) {
log_debug_errno(errno, "Failed to check if dictionary file %s exists, ignoring: %m", path);
return;
}
r = sym_pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, 0);
if (r < 0)
log_debug("Failed to disable libpwquality dictionary check, ignoring: %s",
sym_pwquality_strerror(buf, sizeof(buf), r, NULL));
}
int pwq_allocate_context(pwquality_settings_t **ret) {
_cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
void *auxerror;
int r;
assert(ret);
r = dlopen_pwquality();
if (r < 0)
return r;
pwq = sym_pwquality_default_settings();
if (!pwq)
return -ENOMEM;
r = sym_pwquality_read_config(pwq, NULL, &auxerror);
if (r < 0)
log_debug("Failed to read libpwquality configuration, ignoring: %s",
sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
pwq_maybe_disable_dictionary(pwq);
*ret = TAKE_PTR(pwq);
return 0;
}
#define N_SUGGESTIONS 6
int suggest_passwords(void) {
_cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
_cleanup_strv_free_erase_ char **suggestions = NULL;
_cleanup_(erase_and_freep) char *joined = NULL;
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
size_t i;
int r;
r = pwq_allocate_context(&pwq);
if (ERRNO_IS_NOT_SUPPORTED(r))
return 0;
if (r < 0)
return log_error_errno(r, "Failed to allocate libpwquality context: %m");
suggestions = new0(char*, N_SUGGESTIONS+1);
if (!suggestions)
return log_oom();
for (i = 0; i < N_SUGGESTIONS; i++) {
r = sym_pwquality_generate(pwq, 64, suggestions + i);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring: %s",
sym_pwquality_strerror(buf, sizeof(buf), r, NULL));
}
joined = strv_join(suggestions, " ");
if (!joined)
return log_oom();
log_info("Password suggestions: %s", joined);
return 1;
}
int quality_check_password(const char *password, const char *username, char **ret_error) {
_cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
void *auxerror;
int r;
assert(password);
r = pwq_allocate_context(&pwq);
if (ERRNO_IS_NOT_SUPPORTED(r))
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to allocate libpwquality context: %m");
r = sym_pwquality_check(pwq, password, NULL, username, &auxerror);
if (r < 0) {
if (ret_error) {
_cleanup_free_ char *e = NULL;
e = strdup(sym_pwquality_strerror(buf, sizeof(buf), r, auxerror));
if (!e)
return -ENOMEM;
*ret_error = TAKE_PTR(e);
}
return 0; /* all bad */
}
return 1; /* all good */
}
#endif

View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "macro.h"
#if HAVE_PWQUALITY
/* pwquality.h uses size_t but doesn't include sys/types.h on its own */
#include <sys/types.h>
#include <pwquality.h>
extern int (*sym_pwquality_check)(pwquality_settings_t *pwq, const char *password, const char *oldpassword, const char *user, void **auxerror);
extern pwquality_settings_t *(*sym_pwquality_default_settings)(void);
extern void (*sym_pwquality_free_settings)(pwquality_settings_t *pwq);
extern int (*sym_pwquality_generate)(pwquality_settings_t *pwq, int entropy_bits, char **password);
extern int (*sym_pwquality_get_str_value)(pwquality_settings_t *pwq, int setting, const char **value);
extern int (*sym_pwquality_read_config)(pwquality_settings_t *pwq, const char *cfgfile, void **auxerror);
extern int (*sym_pwquality_set_int_value)(pwquality_settings_t *pwq, int setting, int value);
extern const char* (*sym_pwquality_strerror)(char *buf, size_t len, int errcode, void *auxerror);
int dlopen_pwquality(void);
DEFINE_TRIVIAL_CLEANUP_FUNC(pwquality_settings_t*, sym_pwquality_free_settings);
void pwq_maybe_disable_dictionary(pwquality_settings_t *pwq);
int pwq_allocate_context(pwquality_settings_t **ret);
int suggest_passwords(void);
int quality_check_password(const char *password, const char *username, char **ret_error);
#else
static inline int suggest_passwords(void) {
return 0;
}
static inline int quality_check_password(const char *password, const char *username, char **ret_error) {
if (ret_error)
*ret_error = NULL;
return 1; /* all good */
}
#endif

View File

@ -1581,7 +1581,7 @@ UserStorage user_record_storage(UserRecord *h) {
const char *user_record_file_system_type(UserRecord *h) {
assert(h);
return h->file_system_type ?: "ext4";
return h->file_system_type ?: "btrfs";
}
const char *user_record_skeleton_directory(UserRecord *h) {