mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
parent
23cff6d4fe
commit
2aaf565a2d
@ -134,6 +134,7 @@ int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
|
||||
.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);
|
||||
@ -208,6 +209,7 @@ 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);
|
||||
|
||||
@ -367,6 +369,23 @@ static void home_update_pin_fd(Home *h, HomeState state) {
|
||||
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);
|
||||
|
||||
@ -458,6 +477,7 @@ static void home_set_state(Home *h, HomeState 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)) {
|
||||
@ -612,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;
|
||||
@ -1158,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");
|
||||
@ -1957,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;
|
||||
}
|
||||
|
@ -161,6 +161,9 @@ struct Home {
|
||||
|
||||
/* 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);
|
||||
@ -187,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);
|
||||
|
||||
|
@ -1010,13 +1010,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 +1038,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 +1052,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 +1096,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 +1105,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 +1126,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");
|
||||
|
@ -1706,6 +1706,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"))
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user