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:
commit
de3ef2524e
4
TODO
4
TODO
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 }
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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 "!*"
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
return 0;
|
||||
r = 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",
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,13 +1045,6 @@ 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);
|
||||
|
||||
@ -1047,37 +1059,40 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
|
||||
if (m < 0)
|
||||
return m;
|
||||
|
||||
cmsg_close_all(&mh);
|
||||
|
||||
/* Ensure the size matches what we determined before */
|
||||
if (m != n)
|
||||
if (m != n) {
|
||||
cmsg_close_all(&mh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
CMSG_FOREACH(cmsg, &mh)
|
||||
CMSG_FOREACH(cmsg, &mh) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_CREDENTIALS &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
|
||||
|
||||
memcpy(ret_sender, CMSG_DATA(cmsg), sizeof(struct ucred));
|
||||
found_ucred = true;
|
||||
assert(!sender);
|
||||
sender = (struct ucred*) CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
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;
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
|
||||
/* Ensure the size matches what we determined before */
|
||||
if (m != n)
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
|
||||
cmsg_close_all(&mh);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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"))
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user