1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-24 21:34:08 +03:00

Merge pull request #20968 from poettering/homed-pin

homed: pin+lock homes while logged in + keep trying to unmount on logging out + optionally drop caches on logging out
This commit is contained in:
Lennart Poettering 2021-10-11 23:11:03 +02:00 committed by GitHub
commit de3ef2524e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 547 additions and 118 deletions

4
TODO
View File

@ -1248,10 +1248,6 @@ Features:
fallback logic to get a regular user created on uninitialized systems.
- store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
systemd-cryptsetup, so that it can unlock homed volumes
- try to unmount in regular intervals when home dir was busy when we
tried because idle.
- keep an fd to the homedir open at all times, to keep the fs pinned
(autofs and such) while user is logged in.
* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
specified, synthesize a definition automatically if we can: enlarge last

View File

@ -610,6 +610,17 @@
node is not allowed if any of the other storage backends are used.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--drop-caches=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Automatically flush OS file system caches on logout. This is useful in combination
with the fscrypt storage backend to ensure the OS does not keep decrypted versions of the files and
directories in memory (and accessible) after logout. This option is also supported on other backends,
but should not bring any benefit there. Defaults to off, except if the selected storage backend is
fscrypt, where it defaults to on. Note that flushing OS caches will negatively influence performance
of the OS shortly after logout.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>

View File

@ -551,7 +551,7 @@ int getpeername_pretty(int fd, bool include_port, char **ret) {
return -errno;
if (sa.sa.sa_family == AF_UNIX) {
struct ucred ucred = {};
struct ucred ucred = UCRED_INVALID;
/* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */

View File

@ -327,3 +327,6 @@ static inline int socket_set_recvfragsize(int fd, int af, bool b) {
}
int socket_get_mtu(int fd, int af, size_t *ret);
/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }

View File

@ -1085,3 +1085,14 @@ int is_this_me(const char *username) {
return uid == getuid();
}
const char *get_home_root(void) {
const char *e;
/* For debug purposes allow overriding where we look for home dirs */
e = secure_getenv("SYSTEMD_HOME_ROOT");
if (e && path_is_absolute(e) && path_is_normalized(e))
return e;
return "/home";
}

View File

@ -112,6 +112,8 @@ bool is_nologin_shell(const char *shell);
int is_this_me(const char *username);
const char *get_home_root(void);
/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
#define PASSWORD_LOCKED_AND_INVALID "!*"

View File

@ -2131,6 +2131,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
" subvolume, cifs)\n"
" --image-path=PATH Path to image file/directory\n"
" --drop-caches=BOOL Whether to automatically drop caches on logout\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 (btrfs, ext4, xfs)\n"
@ -2245,6 +2246,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_RECOVERY_KEY,
ARG_AND_RESIZE,
ARG_AND_CHANGE_PASSWORD,
ARG_DROP_CACHES,
};
static const struct option options[] = {
@ -2327,6 +2329,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "recovery-key", required_argument, NULL, ARG_RECOVERY_KEY },
{ "and-resize", required_argument, NULL, ARG_AND_RESIZE },
{ "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
{ "drop-caches", required_argument, NULL, ARG_DROP_CACHES },
{}
};
@ -3450,6 +3453,26 @@ static int parse_argv(int argc, char *argv[]) {
arg_and_change_password = true;
break;
case ARG_DROP_CACHES: {
bool drop_caches;
if (isempty(optarg)) {
r = drop_from_identity("dropCaches");
if (r < 0)
return r;
}
r = parse_boolean_argument("--drop-caches=", optarg, &drop_caches);
if (r < 0)
return r;
r = json_variant_set_field_boolean(&arg_identity_extra, "dropCaches", r);
if (r < 0)
return log_error_errno(r, "Failed to set drop caches field: %m");
break;
}
case '?':
return -EINVAL;

View File

@ -38,6 +38,9 @@
#include "user-record.h"
#include "user-util.h"
/* Retry to deactivate home directories again and again every 15s until it works */
#define RETRY_DEACTIVATE_USEC (15U * USEC_PER_SEC)
#define HOME_USERS_MAX 500
#define PENDING_OPERATIONS_MAX 100
@ -130,6 +133,8 @@ int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
.worker_stdout_fd = -1,
.sysfs = TAKE_PTR(ns),
.signed_locally = -1,
.pin_fd = -1,
.luks_lock_fd = -1,
};
r = hashmap_put(m->homes_by_name, home->user_name, home);
@ -203,6 +208,11 @@ Home *home_free(Home *h) {
h->current_operation = operation_unref(h->current_operation);
safe_close(h->pin_fd);
safe_close(h->luks_lock_fd);
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
return mfree(h);
}
@ -317,6 +327,141 @@ int home_unlink_record(Home *h) {
return 0;
}
static void home_unpin(Home *h) {
assert(h);
if (h->pin_fd < 0)
return;
h->pin_fd = safe_close(h->pin_fd);
log_debug("Successfully closed pin fd on home for %s.", h->user_name);
}
static void home_pin(Home *h) {
const char *path;
assert(h);
if (h->pin_fd >= 0) /* Already pinned? */
return;
path = user_record_home_directory(h->record);
if (!path) {
log_warning("No home directory path to pin for %s, ignoring.", h->user_name);
return;
}
h->pin_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (h->pin_fd < 0) {
log_warning_errno(errno, "Couldn't open home directory '%s' for pinning, ignoring: %m", path);
return;
}
log_debug("Successfully pinned home directory '%s'.", path);
}
static void home_update_pin_fd(Home *h, HomeState state) {
assert(h);
if (state < 0)
state = home_get_state(h);
return HOME_STATE_SHALL_PIN(state) ? home_pin(h) : home_unpin(h);
}
static void home_maybe_close_luks_lock_fd(Home *h, HomeState state) {
assert(h);
if (h->luks_lock_fd < 0)
return;
if (state < 0)
state = home_get_state(h);
/* Keep the lock as long as the home dir is active or has some operation going */
if (HOME_STATE_IS_EXECUTING_OPERATION(state) || HOME_STATE_IS_ACTIVE(state) || state == HOME_LOCKED)
return;
h->luks_lock_fd = safe_close(h->luks_lock_fd);
log_debug("Successfully closed LUKS backing file lock for %s.", h->user_name);
}
static void home_maybe_stop_retry_deactivate(Home *h, HomeState state) {
assert(h);
/* Free the deactivation retry event source if we won't need it anymore. Specifically, we'll free the
* event source whenever the home directory is already deactivated (and we thus where successful) or
* if we start executing an operation that indicates that the home directory is going to be used or
* operated on again. Also, if the home is referenced again stop the timer */
if (HOME_STATE_MAY_RETRY_DEACTIVATE(state) &&
!h->ref_event_source_dont_suspend &&
!h->ref_event_source_please_suspend)
return;
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
}
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error);
static void home_start_retry_deactivate(Home *h);
static int home_on_retry_deactivate(sd_event_source *s, uint64_t usec, void *userdata) {
Home *h = userdata;
HomeState state;
assert(s);
assert(h);
/* 15s after the last attempt to deactivate the home directory passed. Let's try it one more time. */
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
state = home_get_state(h);
if (!HOME_STATE_MAY_RETRY_DEACTIVATE(state))
return 0;
if (IN_SET(state, HOME_ACTIVE, HOME_LINGERING)) {
log_info("Again trying to deactivate home directory.");
/* If we are not executing any operation, let's start deactivating now. Note that this will
* restart our timer again, we are gonna be called again if this doesn't work. */
(void) home_deactivate_internal(h, /* force= */ false, NULL);
} else
/* if we are executing an operation (specifically, area already running a deactivation
* operation), then simply reque the timer, so that we retry again. */
home_start_retry_deactivate(h);
return 0;
}
static void home_start_retry_deactivate(Home *h) {
int r;
assert(h);
assert(h->manager);
/* Alrady allocated? */
if (h->retry_deactivate_event_source)
return;
/* If the home directory is being used now don't start the timer */
if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
return;
r = sd_event_add_time_relative(
h->manager->event,
&h->retry_deactivate_event_source,
CLOCK_MONOTONIC,
RETRY_DEACTIVATE_USEC,
1*USEC_PER_MINUTE,
home_on_retry_deactivate,
h);
if (r < 0)
return (void) log_warning_errno(r, "Failed to install retry-deactivate event source, ignoring: %m");
(void) sd_event_source_set_description(h->retry_deactivate_event_source, "retry-deactivate");
}
static void home_set_state(Home *h, HomeState state) {
HomeState old_state, new_state;
@ -331,6 +476,10 @@ static void home_set_state(Home *h, HomeState state) {
home_state_to_string(old_state),
home_state_to_string(new_state));
home_update_pin_fd(h, new_state);
home_maybe_close_luks_lock_fd(h, new_state);
home_maybe_stop_retry_deactivate(h, new_state);
if (HOME_STATE_IS_EXECUTING_OPERATION(old_state) && !HOME_STATE_IS_EXECUTING_OPERATION(new_state)) {
/* If we just finished executing some operation, process the queue of pending operations. And
* enqueue it for GC too. */
@ -483,6 +632,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
case -EKEYREVOKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name);
case -EADDRINUSE:
return sd_bus_error_setf(error, BUS_ERROR_HOME_IN_USE, "Home %s is currently being used elsewhere.", h->user_name);
}
return 0;
@ -1029,6 +1180,12 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
_exit(EXIT_FAILURE);
}
/* If we haven't locked the device yet, ask for a lock to be taken and be passed back to us via sd_notify(). */
if (setenv("SYSTEMD_LUKS_LOCK", one_zero(h->luks_lock_fd < 0), 1) < 0) {
log_error_errno(errno, "Failed to set $SYSTEMD_LUKS_LOCK: %m");
_exit(EXIT_FAILURE);
}
if (h->manager->default_storage >= 0)
if (setenv("SYSTEMD_HOME_DEFAULT_STORAGE", user_storage_to_string(h->manager->default_storage), 1) < 0) {
log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_STORAGE: %m");
@ -1149,6 +1306,7 @@ int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
case HOME_UNFIXATED:
@ -1190,6 +1348,11 @@ int home_activate(Home *h, UserRecord *secret, sd_bus_error *error) {
return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
case HOME_ACTIVE:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_ACTIVE, "Home %s is already active.", h->user_name);
case HOME_LINGERING:
/* If we are lingering, i.e. active but are supposed to be deactivated, then cancel this
* timer if the user explicitly asks us to be active */
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
return 0;
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
@ -1236,6 +1399,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@ -1245,7 +1409,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
if (r < 0)
return r;
return home_authenticate_internal(h, secret, state == HOME_ACTIVE ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
return home_authenticate_internal(h, secret, HOME_STATE_IS_ACTIVE(state) ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
}
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
@ -1253,12 +1417,22 @@ static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
assert(h);
home_unpin(h); /* unpin so that we can deactivate */
r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL);
if (r < 0)
return r;
/* Operation failed before it even started, reacquire pin fd, if state still dictates so */
home_update_pin_fd(h, _HOME_STATE_INVALID);
else {
home_set_state(h, HOME_DEACTIVATING);
r = 0;
}
home_set_state(h, HOME_DEACTIVATING);
return 0;
/* Let's start a timer to retry deactivation in 15. We'll stop the timer once we manage to deactivate
* the home directory again, or we we start any other operation. */
home_start_retry_deactivate(h);
return r;
}
int home_deactivate(Home *h, bool force, sd_bus_error *error) {
@ -1273,6 +1447,7 @@ int home_deactivate(Home *h, bool force, sd_bus_error *error) {
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@ -1309,6 +1484,7 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_ABSENT:
break;
case HOME_ACTIVE:
case HOME_LINGERING:
case HOME_LOCKED:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
@ -1347,6 +1523,7 @@ int home_remove(Home *h, sd_bus_error *error) {
case HOME_DIRTY:
break;
case HOME_ACTIVE:
case HOME_LINGERING:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
}
@ -1485,6 +1662,7 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@ -1498,7 +1676,7 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
if (r < 0)
return r;
home_set_state(h, state == HOME_ACTIVE ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
return 0;
}
@ -1520,6 +1698,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@ -1568,7 +1747,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
if (r < 0)
return r;
home_set_state(h, state == HOME_ACTIVE ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
return 0;
}
@ -1616,6 +1795,7 @@ int home_passwd(Home *h,
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@ -1681,7 +1861,7 @@ int home_passwd(Home *h,
if (r < 0)
return r;
home_set_state(h, state == HOME_ACTIVE ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
return 0;
}
@ -1700,6 +1880,7 @@ int home_unregister(Home *h, sd_bus_error *error) {
case HOME_DIRTY:
break;
case HOME_ACTIVE:
case HOME_LINGERING:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
}
@ -1727,6 +1908,7 @@ int home_lock(Home *h, sd_bus_error *error) {
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@ -1767,6 +1949,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_ABSENT:
case HOME_INACTIVE:
case HOME_ACTIVE:
case HOME_LINGERING:
case HOME_DIRTY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_LOCKED, "Home %s is not locked.", h->user_name);
case HOME_LOCKED:
@ -1789,7 +1972,7 @@ HomeState home_get_state(Home *h) {
/* Otherwise, let's see if the home directory is mounted. If so, we assume for sure the home
* directory is active */
if (user_record_test_home_directory(h->record) == USER_TEST_MOUNTED)
return HOME_ACTIVE;
return h->retry_deactivate_event_source ? HOME_LINGERING : HOME_ACTIVE;
/* And if we see the image being gone, we report this as absent */
r = user_record_test_image_path(h->record);
@ -1802,28 +1985,49 @@ HomeState home_get_state(Home *h) {
return HOME_INACTIVE;
}
void home_process_notify(Home *h, char **l) {
void home_process_notify(Home *h, char **l, int fd) {
_cleanup_close_ int taken_fd = TAKE_FD(fd);
const char *e;
int error;
int r;
assert(h);
e = strv_env_get(l, "ERRNO");
if (!e) {
log_debug("Got notify message lacking ERRNO= field, ignoring.");
e = strv_env_get(l, "SYSTEMD_LUKS_LOCK_FD");
if (e) {
r = parse_boolean(e);
if (r < 0)
return (void) log_debug_errno(r, "Failed to parse SYSTEMD_LUKS_LOCK_FD value: %m");
if (r > 0) {
if (taken_fd < 0)
return (void) log_debug("Got notify message with SYSTEMD_LUKS_LOCK_FD=1 but no fd passed, ignoring: %m");
safe_close(h->luks_lock_fd);
h->luks_lock_fd = TAKE_FD(taken_fd);
log_debug("Successfully acquired LUKS lock fd from worker.");
/* Immediately check if we actually want to keep it */
home_maybe_close_luks_lock_fd(h, _HOME_STATE_INVALID);
} else {
if (taken_fd >= 0)
return (void) log_debug("Got notify message with SYSTEMD_LUKS_LOCK_FD=0 but fd passed, ignoring: %m");
h->luks_lock_fd = safe_close(h->luks_lock_fd);
}
return;
}
e = strv_env_get(l, "ERRNO");
if (!e)
return (void) log_debug("Got notify message lacking both ERRNO= and SYSTEMD_LUKS_LOCK_FD= field, ignoring.");
r = safe_atoi(e, &error);
if (r < 0) {
log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
return;
}
if (error <= 0) {
log_debug("Error number is out of range: %i", error);
return;
}
if (r < 0)
return (void) log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
if (error <= 0)
return (void) log_debug("Error number is out of range: %i", error);
h->worker_error_code = error;
}
@ -2372,6 +2576,7 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
for_state = HOME_AUTHENTICATING_FOR_ACQUIRE;
call = home_authenticate_internal;
break;
@ -2426,6 +2631,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
r = home_deactivate_internal(h, false, &error);
break;
@ -2470,6 +2676,7 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
log_info("Locking home %s.", h->user_name);
r = home_lock(h, &error);
break;
@ -2514,6 +2721,7 @@ static int home_dispatch_deactivate_all(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
log_info("Deactivating home %s.", h->user_name);
r = home_deactivate_internal(h, false, &error);
break;
@ -2559,6 +2767,7 @@ static int home_dispatch_pipe_eof(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
r = home_deactivate_internal(h, false, &error);
if (r < 0)
log_warning_errno(r, "Failed to deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
@ -2600,6 +2809,7 @@ static int home_dispatch_deactivate_force(Home *h, Operation *o) {
case HOME_ACTIVE:
case HOME_LOCKED:
case HOME_LINGERING:
r = home_deactivate_internal(h, true, &error);
if (r < 0)
log_warning_errno(r, "Failed to forcibly deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
@ -2820,6 +3030,7 @@ static const char* const home_state_table[_HOME_STATE_MAX] = {
[HOME_ACTIVATING_FOR_ACQUIRE] = "activating-for-acquire",
[HOME_DEACTIVATING] = "deactivating",
[HOME_ACTIVE] = "active",
[HOME_LINGERING] = "lingering",
[HOME_LOCKING] = "locking",
[HOME_LOCKED] = "locked",
[HOME_UNLOCKING] = "unlocking",

View File

@ -21,6 +21,7 @@ typedef enum HomeState {
HOME_ACTIVATING_FOR_ACQUIRE, /* activating because Acquire() was called */
HOME_DEACTIVATING,
HOME_ACTIVE, /* logged in right now */
HOME_LINGERING, /* not logged in anymore, but we didn't manage to deactivate (because some process keeps it busy?) but we'll keep trying */
HOME_LOCKING,
HOME_LOCKED,
HOME_UNLOCKING,
@ -43,6 +44,7 @@ typedef enum HomeState {
static inline bool HOME_STATE_IS_ACTIVE(HomeState state) {
return IN_SET(state,
HOME_ACTIVE,
HOME_LINGERING,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
@ -74,6 +76,33 @@ static inline bool HOME_STATE_IS_EXECUTING_OPERATION(HomeState state) {
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
static inline bool HOME_STATE_SHALL_PIN(HomeState state) {
/* Like HOME_STATE_IS_ACTIVE() but HOME_LINGERING is missing! */
return IN_SET(state,
HOME_ACTIVE,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
HOME_AUTHENTICATING_WHILE_ACTIVE,
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
static inline bool HOME_STATE_MAY_RETRY_DEACTIVATE(HomeState state) {
/* Indicates when to leave the deactivate retry timer active */
return IN_SET(state,
HOME_ACTIVE,
HOME_LINGERING,
HOME_DEACTIVATING,
HOME_LOCKING,
HOME_UNLOCKING,
HOME_UNLOCKING_FOR_ACQUIRE,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
HOME_AUTHENTICATING_WHILE_ACTIVE,
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
struct Home {
Manager *manager;
char *user_name;
@ -126,6 +155,15 @@ struct Home {
/* Used to coalesce bus PropertiesChanged events */
sd_event_source *deferred_change_event_source;
/* An fd to the top-level home directory we keep while logged in, to keep the dir busy */
int pin_fd;
/* A time event used to repeatedly try to unmount home dir after use if it didn't work on first try */
sd_event_source *retry_deactivate_event_source;
/* An fd that locks the backing file of LUKS home dirs with a BSD lock. */
int luks_lock_fd;
};
int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret);
@ -152,7 +190,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error);
HomeState home_get_state(Home *h);
void home_process_notify(Home *h, char **l);
void home_process_notify(Home *h, char **l, int fd);
int home_killall(Home *h);

View File

@ -83,35 +83,38 @@ static void manager_watch_home(Manager *m) {
m->inotify_event_source = sd_event_source_disable_unref(m->inotify_event_source);
m->scan_slash_home = false;
if (statfs("/home/", &sfs) < 0) {
if (statfs(get_home_root(), &sfs) < 0) {
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to statfs() /home/ directory, disabling automatic scanning.");
"Failed to statfs() %s directory, disabling automatic scanning.", get_home_root());
return;
}
if (is_network_fs(&sfs)) {
log_info("/home/ is a network file system, disabling automatic scanning.");
log_info("%s is a network file system, disabling automatic scanning.", get_home_root());
return;
}
if (is_fs_type(&sfs, AUTOFS_SUPER_MAGIC)) {
log_info("/home/ is on autofs, disabling automatic scanning.");
log_info("%s is on autofs, disabling automatic scanning.", get_home_root());
return;
}
m->scan_slash_home = true;
r = sd_event_add_inotify(m->event, &m->inotify_event_source, "/home/",
r = sd_event_add_inotify(m->event, &m->inotify_event_source, get_home_root(),
IN_CREATE|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF|IN_ONLYDIR|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE,
on_home_inotify, m);
if (r < 0)
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to create inotify watch on /home/, ignoring.");
"Failed to create inotify watch on %s, ignoring.", get_home_root());
(void) sd_event_source_set_description(m->inotify_event_source, "home-inotify");
log_info("Watching %s.", get_home_root());
}
static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
_cleanup_free_ char *j = NULL;
Manager *m = userdata;
const char *e, *n;
@ -121,15 +124,15 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
if ((event->mask & (IN_Q_OVERFLOW|IN_MOVE_SELF|IN_DELETE_SELF|IN_IGNORED|IN_UNMOUNT)) != 0) {
if (FLAGS_SET(event->mask, IN_Q_OVERFLOW))
log_debug("/home/ inotify queue overflow, rescanning.");
log_debug("%s inotify queue overflow, rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_MOVE_SELF))
log_info("/home/ moved or renamed, recreating watch and rescanning.");
log_info("%s moved or renamed, recreating watch and rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_DELETE_SELF))
log_info("/home/ deleted, recreating watch and rescanning.");
log_info("%s deleted, recreating watch and rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_UNMOUNT))
log_info("/home/ unmounted, recreating watch and rescanning.");
log_info("%s unmounted, recreating watch and rescanning.", get_home_root());
else if (FLAGS_SET(event->mask, IN_IGNORED))
log_info("/home/ watch invalidated, recreating watch and rescanning.");
log_info("%s watch invalidated, recreating watch and rescanning.", get_home_root());
manager_watch_home(m);
(void) manager_gc_images(m);
@ -150,15 +153,19 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
if (!suitable_user_name(n))
return 0;
j = path_join(get_home_root(), event->name);
if (!j)
return log_oom();
if ((event->mask & (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)) != 0) {
if (FLAGS_SET(event->mask, IN_CREATE))
log_debug("/home/%s has been created, having a look.", event->name);
log_debug("%s has been created, having a look.", j);
else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
log_debug("/home/%s has been modified, having a look.", event->name);
log_debug("%s has been modified, having a look.", j);
else if (FLAGS_SET(event->mask, IN_MOVED_TO))
log_debug("/home/%s has been moved in, having a look.", event->name);
log_debug("%s has been moved in, having a look.", j);
(void) manager_assess_image(m, -1, "/home/", event->name);
(void) manager_assess_image(m, -1, get_home_root(), event->name);
(void) bus_manager_emit_auto_login_changed(m);
}
@ -166,11 +173,11 @@ static int on_home_inotify(sd_event_source *s, const struct inotify_event *event
Home *h;
if (FLAGS_SET(event->mask, IN_DELETE))
log_debug("/home/%s has been deleted, revalidating.", event->name);
log_debug("%s has been deleted, revalidating.", j);
else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
log_debug("/home/%s has been closed after writing, revalidating.", event->name);
log_debug("%s has been closed after writing, revalidating.", j);
else if (FLAGS_SET(event->mask, IN_MOVED_FROM))
log_debug("/home/%s has been moved away, revalidating.", event->name);
log_debug("%s has been moved away, revalidating.", j);
h = hashmap_get(m->homes_by_name, n);
if (h) {
@ -487,7 +494,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
* comprehensive, but should cover most cases. Note that in an ideal world every user would be
* registered in NSS and avoid our own UID range, but for all other cases, it's a good idea to be
* paranoid and check quota if we can. */
FOREACH_STRING(where, "/home/", "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
FOREACH_STRING(where, get_home_root(), "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
struct dqblk req;
struct stat st;
@ -914,13 +921,13 @@ int manager_enumerate_images(Manager *m) {
if (!m->scan_slash_home)
return 0;
d = opendir("/home/");
d = opendir(get_home_root());
if (!d)
return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
"Failed to open /home/: %m");
"Failed to open %s: %m", get_home_root());
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read /home/ directory: %m"))
(void) manager_assess_image(m, dirfd(d), "/home", de->d_name);
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s directory: %m", get_home_root()))
(void) manager_assess_image(m, dirfd(d), get_home_root(), de->d_name);
return 0;
}
@ -1010,13 +1017,25 @@ static int manager_bind_varlink(Manager *m) {
return 0;
}
static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
static ssize_t read_datagram(
int fd,
struct ucred *ret_sender,
void **ret,
int *ret_passed_fd) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))) control;
_cleanup_free_ void *buffer = NULL;
_cleanup_close_ int passed_fd = -1;
struct ucred *sender = NULL;
struct cmsghdr *cmsg;
struct msghdr mh;
struct iovec iov;
ssize_t n, m;
assert(fd >= 0);
assert(ret_sender);
assert(ret);
assert(ret_passed_fd);
n = next_datagram_size_fd(fd);
if (n < 0)
@ -1026,58 +1045,54 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
if (!buffer)
return -ENOMEM;
if (ret_sender) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
bool found_ucred = false;
struct cmsghdr *cmsg;
struct msghdr mh;
struct iovec iov;
/* Pass one extra byte, as a size check */
iov = IOVEC_MAKE(buffer, n + 1);
/* Pass one extra byte, as a size check */
iov = IOVEC_MAKE(buffer, n + 1);
mh = (struct msghdr) {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
mh = (struct msghdr) {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (m < 0)
return m;
m = recvmsg_safe(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (m < 0)
return m;
/* Ensure the size matches what we determined before */
if (m != n) {
cmsg_close_all(&mh);
return -EMSGSIZE;
}
/* Ensure the size matches what we determined before */
if (m != n)
return -EMSGSIZE;
CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
assert(!sender);
sender = (struct ucred*) CMSG_DATA(cmsg);
}
CMSG_FOREACH(cmsg, &mh)
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
memcpy(ret_sender, CMSG_DATA(cmsg), sizeof(struct ucred));
found_ucred = true;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
cmsg_close_all(&mh);
return -EMSGSIZE;
}
if (!found_ucred)
*ret_sender = (struct ucred) {
.pid = 0,
.uid = UID_INVALID,
.gid = GID_INVALID,
};
} else {
m = recv(fd, buffer, n + 1, MSG_DONTWAIT);
if (m < 0)
return -errno;
/* Ensure the size matches what we determined before */
if (m != n)
return -EMSGSIZE;
assert(passed_fd < 0);
passed_fd = *(int*) CMSG_DATA(cmsg);
}
}
if (sender)
*ret_sender = *sender;
else
*ret_sender = (struct ucred) UCRED_INVALID;
*ret_passed_fd = TAKE_FD(passed_fd);
/* For safety reasons: let's always NUL terminate. */
((char*) buffer)[n] = 0;
*ret = TAKE_PTR(buffer);
@ -1088,7 +1103,8 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ void *datagram = NULL;
struct ucred sender;
_cleanup_close_ int passed_fd = -1;
struct ucred sender = UCRED_INVALID;
Manager *m = userdata;
ssize_t n;
Home *h;
@ -1096,7 +1112,7 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *
assert(s);
assert(m);
n = read_datagram(fd, &sender, &datagram);
n = read_datagram(fd, &sender, &datagram, &passed_fd);
if (IN_SET(n, -EAGAIN, -EINTR))
return 0;
if (n < 0)
@ -1117,7 +1133,7 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *
if (!l)
return log_oom();
home_process_notify(h, l);
home_process_notify(h, l, TAKE_FD(passed_fd));
return 0;
}

View File

@ -8,11 +8,14 @@
#include <sys/mount.h>
#include <sys/xattr.h>
#include "sd-daemon.h"
#include "blkid-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "chattr-util.h"
#include "dm-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
@ -1042,6 +1045,40 @@ int run_fallocate_by_path(const char *backing_path) {
return run_fallocate(backing_fd, NULL);
}
static int lock_image_fd(int image_fd, const char *ip) {
int r;
/* If the $SYSTEMD_LUKS_LOCK environment variable is set we'll take an exclusive BSD lock on the
* image file, and send it to our parent. homed will keep it open to ensure no other instance of
* homed (across the network or such) will also mount the file. */
r = getenv_bool("SYSTEMD_LUKS_LOCK");
if (r == -ENXIO)
return 0;
if (r < 0)
return log_error_errno(r, "Failed to parse $SYSTEMD_LUKS_LOCK environment variable: %m");
if (r > 0) {
if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) {
if (errno == EWOULDBLOCK)
log_error_errno(errno, "Image file '%s' already locked, can't use.", ip);
else
log_error_errno(errno, "Failed to lock image file '%s': %m", ip);
return errno != EWOULDBLOCK ? -errno : -EADDRINUSE; /* Make error recognizable */
}
log_info("Successfully locked image file '%s'.", ip);
/* Now send it to our parent to keep safe while the home dir is active */
r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1);
if (r < 0)
log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m");
}
return 0;
}
int home_prepare_luks(
UserRecord *h,
bool already_activated,
@ -1176,6 +1213,10 @@ int home_prepare_luks(
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 = lock_image_fd(image_fd, ip);
if (r < 0)
return r;
r = luks_validate(image_fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
if (r < 0)
return log_error_errno(r, "Failed to validate disk label: %m");

View File

@ -28,6 +28,7 @@
#include "rm-rf.h"
#include "stat-util.h"
#include "strv.h"
#include "sync-util.h"
#include "tmpfile-util.h"
#include "user-util.h"
#include "virt.h"
@ -283,6 +284,20 @@ int user_record_authenticate(
return 0;
}
static void drop_caches_now(void) {
int r;
/* Drop file system caches now. See https://www.kernel.org/doc/Documentation/sysctl/vm.txt for
* details. We write "2" into /proc/sys/vm/drop_caches to ensure dentries/inodes are flushed, but not
* more. */
r = write_string_file("/proc/sys/vm/drop_caches", "2\n", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_warning_errno(r, "Failed to drop caches, ignoring: %m");
else
log_debug("Dropped caches.");
}
int home_setup_undo(HomeSetup *setup) {
int r = 0, q;
@ -295,6 +310,9 @@ int home_setup_undo(HomeSetup *setup) {
r = q;
}
if (syncfs(setup->root_fd) < 0)
log_debug_errno(errno, "Failed to synchronize home directory, ignoring: %m");
setup->root_fd = safe_close(setup->root_fd);
}
@ -345,6 +363,9 @@ int home_setup_undo(HomeSetup *setup) {
setup->volume_key = mfree(setup->volume_key);
setup->volume_key_size = 0;
if (setup->do_drop_caches)
drop_caches_now();
return r;
}
@ -367,6 +388,9 @@ int home_prepare(
/* Makes a home directory accessible (through the root_fd file descriptor, not by path!). */
if (!already_activated) /* If we set up the directory, we should also drop caches once we are done */
setup->do_drop_caches = setup->do_drop_caches || user_record_drop_caches(h);
switch (user_record_storage(h)) {
case USER_LUKS:
@ -827,6 +851,13 @@ static int home_deactivate(UserRecord *h, bool force) {
return r;
}
/* Sync explicitly, so that the drop caches logic below can work as documented */
r = syncfs_path(AT_FDCWD, user_record_home_directory(h));
if (r < 0)
log_debug_errno(r, "Failed to synchronize home directory, ignoring: %m");
else
log_info("Syncing completed.");
if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
@ -846,6 +877,9 @@ static int home_deactivate(UserRecord *h, bool force) {
if (!done)
return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home is not active.");
if (user_record_drop_caches(h))
drop_caches_now();
log_info("Everything completed.");
return 0;
}
@ -1095,12 +1129,12 @@ static int determine_default_storage(UserStorage *ret) {
if (r < 0)
return log_error_errno(r, "Failed to determine whether we are in a container: %m");
if (r == 0) {
r = path_is_encrypted("/home");
r = path_is_encrypted(get_home_root());
if (r > 0)
log_info("/home is encrypted, not using '%s' storage, in order to avoid double encryption.", user_storage_to_string(USER_LUKS));
log_info("%s is encrypted, not using '%s' storage, in order to avoid double encryption.", get_home_root(), user_storage_to_string(USER_LUKS));
else {
if (r < 0)
log_warning_errno(r, "Failed to determine if /home is encrypted, ignoring: %m");
log_warning_errno(r, "Failed to determine if %s is encrypted, ignoring: %m", get_home_root());
r = dlopen_cryptsetup();
if (r < 0)
@ -1114,14 +1148,14 @@ static int determine_default_storage(UserStorage *ret) {
} else
log_info("Running in container, not using '%s' storage.", user_storage_to_string(USER_LUKS));
r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
r = path_is_fs_type(get_home_root(), BTRFS_SUPER_MAGIC);
if (r < 0)
log_warning_errno(r, "Failed to determine file system of /home, ignoring: %m");
log_warning_errno(r, "Failed to determine file system of %s, ignoring: %m", get_home_root());
if (r > 0) {
log_info("/home is on btrfs, using '%s' as storage.", user_storage_to_string(USER_SUBVOLUME));
log_info("%s is on btrfs, using '%s' as storage.", get_home_root(), user_storage_to_string(USER_SUBVOLUME));
*ret = USER_SUBVOLUME;
} else {
log_info("/home is on simple file system, using '%s' as storage.", user_storage_to_string(USER_DIRECTORY));
log_info("%s is on simple file system, using '%s' as storage.", get_home_root(), user_storage_to_string(USER_DIRECTORY));
*ret = USER_DIRECTORY;
}
@ -1268,9 +1302,21 @@ static int home_remove(UserRecord *h) {
if (unlink(ip) < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to remove %s: %m", ip);
} else
} else {
_cleanup_free_ char *parent = NULL;
deleted = true;
r = path_extract_directory(ip, &parent);
if (r < 0)
log_debug_errno(r, "Failed to determine parent directory of '%s': %m", ip);
else {
r = fsync_path_at(AT_FDCWD, parent);
if (r < 0)
log_debug_errno(r, "Failed to synchronize disk after deleting '%s', ignoring: %m", ip);
}
}
} else if (S_ISBLK(st.st_mode))
log_info("Not removing file system on block device %s.", ip);
else
@ -1285,7 +1331,7 @@ static int home_remove(UserRecord *h) {
case USER_FSCRYPT:
assert(ip);
r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_SYNCFS);
if (r < 0) {
if (r != -ENOENT)
return log_warning_errno(r, "Failed to remove %s: %m", ip);
@ -1316,9 +1362,12 @@ static int home_remove(UserRecord *h) {
deleted = true;
}
if (deleted)
if (deleted) {
if (user_record_drop_caches(h))
drop_caches_now();
log_info("Everything completed.");
else
} else
return log_notice_errno(SYNTHETIC_ERRNO(EALREADY),
"Nothing to remove.");
@ -1706,6 +1755,7 @@ static int run(int argc, char *argv[]) {
* ENOEXEC file system is currently not active
* ENOSPC not enough disk space for operation
* EKEYREVOKED user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate
* EADDRINUSE home image is already used elsewhere (lock taken)
*/
if (streq(argv[1], "activate"))

View File

@ -32,6 +32,7 @@ typedef struct HomeSetup {
bool do_offline_fitrim;
bool do_offline_fallocate;
bool do_mark_clean;
bool do_drop_caches;
uint64_t partition_offset;
uint64_t partition_size;

View File

@ -75,7 +75,7 @@ int user_record_synthesize(
if (!ip)
return -ENOMEM;
hd = path_join("/home/", user_name);
hd = path_join(get_home_root(), user_name);
if (!hd)
return -ENOMEM;

View File

@ -36,6 +36,7 @@
#include "syslog-util.h"
#include "tmpfile-util.h"
#include "unit-name.h"
#include "user-util.h"
#define STDOUT_STREAMS_MAX 4096
@ -663,6 +664,7 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
*stream = (StdoutStream) {
.fd = -1,
.priority = LOG_INFO,
.ucred = UCRED_INVALID,
};
xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
@ -727,9 +729,9 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
}
if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
struct ucred u;
struct ucred u = UCRED_INVALID;
r = getpeercred(fd, &u);
(void) getpeercred(fd, &u);
/* By closing fd here we make sure that the client won't wait too long for journald to
* gather all the data it adds to the error message to find out that the connection has
@ -737,7 +739,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
*/
fd = safe_close(fd);
server_driver_message(s, r < 0 ? 0 : u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
server_driver_message(s, u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL);
return 0;
}

View File

@ -142,6 +142,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE),
SD_BUS_ERROR_MAP_END
};

View File

@ -126,5 +126,6 @@
#define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
#define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse"
BUS_ERROR_MAP_ELF_USE(bus_common_errors);

View File

@ -249,6 +249,7 @@ _public_ int sd_bus_new(sd_bus **ret) {
.original_pid = getpid_cached(),
.n_groups = SIZE_MAX,
.close_on_exit = true,
.ucred = UCRED_INVALID,
};
/* We guarantee that wqueue always has space for at least one entry */

View File

@ -136,7 +136,7 @@ _public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) {
}
_public_ int sd_peer_get_session(int fd, char **session) {
struct ucred ucred = {};
struct ucred ucred = UCRED_INVALID;
int r;
assert_return(fd >= 0, -EBADF);

View File

@ -250,6 +250,9 @@ int rm_rf_children(
ret = r;
}
if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0)
ret = -errno;
return ret;
}

View File

@ -14,6 +14,7 @@ typedef enum RemoveFlags {
REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete or access something */
REMOVE_CHMOD_RESTORE = 1 << 6, /* Restore the old mode before returning */
REMOVE_SYNCFS = 1 << 7, /* syncfs() the root of the specified directory after removing everything in it */
} RemoveFlags;
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags);

View File

@ -435,6 +435,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
if (hr->password_change_now >= 0)
printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
if (hr->drop_caches >= 0 || user_record_drop_caches(hr))
printf(" Drop Caches: %s\n", yes_no(user_record_drop_caches(hr)));
if (!strv_isempty(hr->ssh_authorized_keys))
printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));

View File

@ -202,6 +202,7 @@ UserRecord* user_record_new(void) {
.pkcs11_protected_authentication_path_permitted = -1,
.fido2_user_presence_permitted = -1,
.fido2_user_verification_permitted = -1,
.drop_caches = -1,
};
return h;
@ -1284,6 +1285,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp
{ "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
{ "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
{ "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
{ "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 },
{ "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
{ "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
{ "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
@ -1406,11 +1408,11 @@ int user_record_build_image_path(UserStorage storage, const char *user_name_and_
return 0;
}
z = strjoin("/home/", user_name_and_realm, suffix);
z = strjoin(get_home_root(), "/", user_name_and_realm, suffix);
if (!z)
return -ENOMEM;
*ret = z;
*ret = path_simplify(z);
return 1;
}
@ -1435,7 +1437,7 @@ static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
return 0;
if (!h->home_directory && !h->home_directory_auto) {
h->home_directory_auto = path_join("/home/", h->user_name);
h->home_directory_auto = path_join(get_home_root(), h->user_name);
if (!h->home_directory_auto)
return json_log_oom(h->json, json_flags);
}
@ -1620,7 +1622,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
{ "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
{ "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
{ "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 },
{ "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
{ "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
{ "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
{ "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
{ "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
@ -1629,6 +1631,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla
{ "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
{ "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
{ "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
{ "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 },
{ "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
{ "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
{ "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
@ -2021,6 +2024,16 @@ bool user_record_can_authenticate(UserRecord *h) {
return !strv_isempty(h->hashed_password);
}
bool user_record_drop_caches(UserRecord *h) {
assert(h);
if (h->drop_caches >= 0)
return h->drop_caches;
/* By default drop caches on fscrypt, not otherwise. */
return user_record_storage(h) == USER_FSCRYPT;
}
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
assert(h);

View File

@ -353,6 +353,7 @@ typedef struct UserRecord {
int removable;
int enforce_password_policy;
int auto_login;
int drop_caches;
uint64_t stop_delay_usec; /* How long to leave systemd --user around on log-out */
int kill_processes; /* Whether to kill user processes forcibly on log-out */
@ -419,6 +420,7 @@ int user_record_removable(UserRecord *h);
usec_t user_record_ratelimit_interval_usec(UserRecord *h);
uint64_t user_record_ratelimit_burst(UserRecord *h);
bool user_record_can_authenticate(UserRecord *h);
bool user_record_drop_caches(UserRecord *h);
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret);

View File

@ -258,8 +258,7 @@ static int varlink_new(Varlink **ret) {
.state = _VARLINK_STATE_INVALID,
.ucred.uid = UID_INVALID,
.ucred.gid = GID_INVALID,
.ucred = UCRED_INVALID,
.timestamp = USEC_INFINITY,
.timeout = VARLINK_DEFAULT_TIMEOUT_USEC
@ -2077,7 +2076,7 @@ static int validate_connection(VarlinkServer *server, const struct ucred *ucred)
return 1;
}
static int count_connection(VarlinkServer *server, struct ucred *ucred) {
static int count_connection(VarlinkServer *server, const struct ucred *ucred) {
unsigned c;
int r;
@ -2106,8 +2105,8 @@ static int count_connection(VarlinkServer *server, struct ucred *ucred) {
int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret) {
_cleanup_(varlink_unrefp) Varlink *v = NULL;
struct ucred ucred = UCRED_INVALID;
bool ucred_acquired;
struct ucred ucred;
int r;
assert_return(server, -EINVAL);