]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/home/homed-home.c
homed: explicitly deactivate all home directories on shutdown
[thirdparty/systemd.git] / src / home / homed-home.c
index 09afbc70c3fe0433bdce9909f4aca763632be032..6d0f0fbd0e52b53d41365129851a3040c803ef00 100644 (file)
@@ -33,6 +33,7 @@
 #include "strv.h"
 #include "user-record-sign.h"
 #include "user-record-util.h"
+#include "user-record-pwquality.h"
 #include "user-record.h"
 #include "user-util.h"
 
@@ -292,7 +293,7 @@ int home_save_record(Home *h) {
 
         fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
 
-        r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600);
+        r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600|WRITE_STRING_FILE_SYNC);
         if (r < 0)
                 return r;
 
@@ -436,9 +437,9 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
         switch (e) {
 
         case -EMSGSIZE:
-                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrinked");
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk");
         case -ETXTBSY:
-                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrinked offline");
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline");
         case -ERANGE:
                 return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
         case -ENOLINK:
@@ -453,10 +454,16 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD, "Password for home %s is incorrect or not sufficient for authentication.", h->user_name);
         case -EBADSLT:
                 return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, "Password for home %s is incorrect or not sufficient, and configured security token not found either.", h->user_name);
+        case -EREMOTEIO:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_RECOVERY_KEY, "Recovery key for home %s is incorrect or not sufficient for authentication.", h->user_name);
         case -ENOANO:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
         case -ERFKILL:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
+        case -EMEDIUMTYPE:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
+        case -ENOSTR:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
         case -EOWNERDEAD:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
         case -ENOLCK:
@@ -471,6 +478,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name);
         case -ENOSPC:
                 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);
         }
 
         return 0;
@@ -1002,13 +1011,33 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
         if (r < 0)
                 return r;
         if (r == 0) {
+                const char *homework, *suffix, *unix_path;
+
                 /* Child */
 
-                if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
+                suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
+                if (suffix)
+                        unix_path = strjoina("/run/systemd/home/notify.", suffix);
+                else
+                        unix_path = "/run/systemd/home/notify";
+
+                if (setenv("NOTIFY_SOCKET", unix_path, 1) < 0) {
                         log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %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");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                if (h->manager->default_file_system_type)
+                        if (setenv("SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE", h->manager->default_file_system_type, 1) < 0) {
+                                log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
                 r = rearrange_stdio(stdin_fd, stdout_fd, STDERR_FILENO);
                 if (r < 0) {
                         log_error_errno(r, "Failed to rearrange stdin/stdout/stderr: %m");
@@ -1017,8 +1046,12 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
 
                 stdin_fd = stdout_fd = -1; /* have been invalidated by rearrange_stdio() */
 
-                execl(SYSTEMD_HOMEWORK_PATH, SYSTEMD_HOMEWORK_PATH, verb, NULL);
-                log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
+                /* Allow overriding the homework path via an environment variable, to make debugging
+                 * easier. */
+                homework = getenv("SYSTEMD_HOMEWORK_PATH") ?: SYSTEMD_HOMEWORK_PATH;
+
+                execl(homework, homework, verb, NULL);
+                log_error_errno(errno, "Failed to invoke %s: %m", homework);
                 _exit(EXIT_FAILURE);
         }
 
@@ -1088,7 +1121,7 @@ static int home_fixate_internal(
         if (r < 0)
                 return r;
 
-        if (for_state == HOME_FIXATING_FOR_ACTIVATION) {
+        if (IN_SET(for_state, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE)) {
                 /* Remember the secret data, since we need it for the activation again, later on. */
                 user_record_unref(h->secret);
                 h->secret = user_record_ref(secret);
@@ -1107,6 +1140,7 @@ int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error) {
         case HOME_ABSENT:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
         case HOME_INACTIVE:
+        case HOME_DIRTY:
         case HOME_ACTIVE:
         case HOME_LOCKED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
@@ -1152,6 +1186,7 @@ int home_activate(Home *h, UserRecord *secret, 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_INACTIVE:
+        case HOME_DIRTY:
                 break;
         default:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1192,6 +1227,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
         case HOME_UNFIXATED:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
         case HOME_ACTIVE:
                 break;
         default:
@@ -1225,6 +1261,7 @@ int home_deactivate(Home *h, bool force, sd_bus_error *error) {
         case HOME_UNFIXATED:
         case HOME_ABSENT:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
         case HOME_LOCKED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
@@ -1243,16 +1280,24 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
         assert(h);
 
         switch (home_get_state(h)) {
-        case HOME_INACTIVE:
+        case HOME_INACTIVE: {
+                int t;
+
                 if (h->record->storage < 0)
                         break; /* if no storage is defined we don't know what precisely to look for, hence
                                 * HOME_INACTIVE is OK in that case too. */
 
-                if (IN_SET(user_record_test_image_path(h->record), USER_TEST_MAYBE, USER_TEST_UNDEFINED))
+                t = user_record_test_image_path(h->record);
+                if (IN_SET(t, USER_TEST_MAYBE, USER_TEST_UNDEFINED))
                         break; /* And if the image path test isn't conclusive, let's also go on */
 
-                _fallthrough_;
+                if (IN_SET(t, -EBADFD, -ENOTDIR))
+                        return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Selected home image of user %s already exists or has wrong inode type.", h->user_name);
+
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Selected home image of user %s already exists.", h->user_name);
+        }
         case HOME_UNFIXATED:
+        case HOME_DIRTY:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Home of user %s already exists.", h->user_name);
         case HOME_ABSENT:
                 break;
@@ -1265,7 +1310,7 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
         if (h->record->enforce_password_policy == false)
                 log_debug("Password quality check turned off for account, skipping.");
         else {
-                r = quality_check_password(h->record, secret, error);
+                r = user_record_quality_check_password(h->record, secret, error);
                 if (r < 0)
                         return r;
         }
@@ -1292,6 +1337,7 @@ int home_remove(Home *h, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
         case HOME_UNFIXATED:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 break;
         case HOME_ACTIVE:
         default:
@@ -1337,7 +1383,13 @@ static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_bind
         return 0;
 }
 
-static int home_update_internal(Home *h, const char *verb, UserRecord *hr, UserRecord *secret, sd_bus_error *error) {
+static int home_update_internal(
+                Home *h,
+                const char *verb,
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
         _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL, *saved_secret = NULL, *signed_hr = NULL;
         int r, c;
 
@@ -1424,6 +1476,7 @@ int home_update(Home *h, UserRecord *hr, 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_INACTIVE:
+        case HOME_DIRTY:
         case HOME_ACTIVE:
                 break;
         default:
@@ -1458,6 +1511,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
         case HOME_LOCKED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
         case HOME_INACTIVE:
+        case HOME_DIRTY:
         case HOME_ACTIVE:
                 break;
         default:
@@ -1520,8 +1574,8 @@ static int home_may_change_password(
         assert(h);
 
         r = user_record_test_password_change_required(h->record);
-        if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED))
-                return 0; /* expired in some form, but chaning is allowed */
+        if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED, -ESTALE))
+                return 0; /* expired in some form, but changing is allowed */
         if (IN_SET(r, -EKEYREJECTED, -EROFS))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name);
         if (r < 0)
@@ -1553,6 +1607,7 @@ int home_passwd(Home *h,
         case HOME_LOCKED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
         case HOME_INACTIVE:
+        case HOME_DIRTY:
         case HOME_ACTIVE:
                 break;
         default:
@@ -1610,7 +1665,7 @@ int home_passwd(Home *h,
         if (c->enforce_password_policy == false)
                 log_debug("Password quality check turned off for account, skipping.");
         else {
-                r = quality_check_password(c, merged_secret, error);
+                r = user_record_quality_check_password(c, merged_secret, error);
                 if (r < 0)
                         return r;
         }
@@ -1635,6 +1690,7 @@ int home_unregister(Home *h, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
         case HOME_ABSENT:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 break;
         case HOME_ACTIVE:
         default:
@@ -1659,6 +1715,7 @@ int home_lock(Home *h, sd_bus_error *error) {
         case HOME_UNFIXATED:
         case HOME_ABSENT:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is not active.", h->user_name);
         case HOME_LOCKED:
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
@@ -1703,6 +1760,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
         case HOME_ABSENT:
         case HOME_INACTIVE:
         case HOME_ACTIVE:
+        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:
                 break;
@@ -1714,6 +1772,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
 }
 
 HomeState home_get_state(Home *h) {
+        int r;
         assert(h);
 
         /* When the state field is initialized, it counts. */
@@ -1726,8 +1785,11 @@ HomeState home_get_state(Home *h) {
                 return HOME_ACTIVE;
 
         /* And if we see the image being gone, we report this as absent */
-        if (user_record_test_image_path(h->record) == USER_TEST_ABSENT)
+        r = user_record_test_image_path(h->record);
+        if (r == USER_TEST_ABSENT)
                 return HOME_ABSENT;
+        if (r == USER_TEST_DIRTY)
+                return HOME_DIRTY;
 
         /* And for all other cases we return "inactive". */
         return HOME_INACTIVE;
@@ -1748,7 +1810,7 @@ void home_process_notify(Home *h, char **l) {
 
         r = safe_atoi(e, &error);
         if (r < 0) {
-                log_debug_errno(r, "Failed to parse receieved error number, ignoring: %s", e);
+                log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
                 return;
         }
         if (error <= 0) {
@@ -2293,6 +2355,7 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
                 break;
 
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 for_state = HOME_ACTIVATING_FOR_ACQUIRE;
                 call = home_activate_internal;
                 break;
@@ -2346,7 +2409,8 @@ static int home_dispatch_release(Home *h, Operation *o) {
                 case HOME_UNFIXATED:
                 case HOME_ABSENT:
                 case HOME_INACTIVE:
-                        r = 0; /* done */
+                case HOME_DIRTY:
+                        r = 1; /* done */
                         break;
 
                 case HOME_LOCKED:
@@ -2366,7 +2430,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
 
         assert(!h->current_operation);
 
-        if (r <= 0) /* failure or completed */
+        if (r != 0) /* failure or completed */
                 operation_result(o, r, &error);
         else /* ongoing */
                 h->current_operation = operation_ref(o);
@@ -2387,13 +2451,14 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
         case HOME_UNFIXATED:
         case HOME_ABSENT:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 log_info("Home %s is not active, no locking necessary.", h->user_name);
-                r = 0; /* done */
+                r = 1; /* done */
                 break;
 
         case HOME_LOCKED:
                 log_info("Home %s is already locked.", h->user_name);
-                r = 0; /* done */
+                r = 1; /* done */
                 break;
 
         case HOME_ACTIVE:
@@ -2417,6 +2482,50 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
         return 1;
 }
 
+static int home_dispatch_deactivate_all(Home *h, Operation *o) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(o);
+        assert(o->type == OPERATION_DEACTIVATE_ALL);
+
+        switch (home_get_state(h)) {
+
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+        case HOME_DIRTY:
+                log_info("Home %s is already deactivated.", h->user_name);
+                r = 1; /* done */
+                break;
+
+        case HOME_LOCKED:
+                log_info("Home %s is currently locked, not deactivating.", h->user_name);
+                r = 1; /* done */
+                break;
+
+        case HOME_ACTIVE:
+                log_info("Deactivating home %s.", h->user_name);
+                r = home_deactivate_internal(h, false, &error);
+                break;
+
+        default:
+                /* All other cases means we are currently executing an operation, which means the job remains
+                 * pending. */
+                return 0;
+        }
+
+        assert(!h->current_operation);
+
+        if (r != 0) /* failure or completed */
+                operation_result(o, r, &error);
+        else /* ongoing */
+                h->current_operation = operation_ref(o);
+
+        return 1;
+}
+
 static int home_dispatch_pipe_eof(Home *h, Operation *o) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
@@ -2433,6 +2542,7 @@ static int home_dispatch_pipe_eof(Home *h, Operation *o) {
         case HOME_UNFIXATED:
         case HOME_ABSENT:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 log_info("Home %s already deactivated, no automatic deactivation needed.", h->user_name);
                 break;
 
@@ -2472,6 +2582,7 @@ static int home_dispatch_deactivate_force(Home *h, Operation *o) {
         case HOME_UNFIXATED:
         case HOME_ABSENT:
         case HOME_INACTIVE:
+        case HOME_DIRTY:
                 log_debug("Home %s already deactivated, no forced deactivation due to unplug needed.", h->user_name);
                 break;
 
@@ -2512,6 +2623,7 @@ static int on_pending(sd_event_source *s, void *userdata) {
                         [OPERATION_ACQUIRE]          = home_dispatch_acquire,
                         [OPERATION_RELEASE]          = home_dispatch_release,
                         [OPERATION_LOCK_ALL]         = home_dispatch_lock_all,
+                        [OPERATION_DEACTIVATE_ALL]   = home_dispatch_deactivate_all,
                         [OPERATION_PIPE_EOF]         = home_dispatch_pipe_eof,
                         [OPERATION_DEACTIVATE_FORCE] = home_dispatch_deactivate_force,
                 };
@@ -2679,10 +2791,24 @@ int home_set_current_message(Home *h, sd_bus_message *m) {
         return 1;
 }
 
+int home_wait_for_worker(Home *h) {
+        assert(h);
+
+        if (h->worker_pid <= 0)
+                return 0;
+
+        log_info("Worker process for home %s is still running while exiting. Waiting for it to finish.", h->user_name);
+        (void) wait_for_terminate(h->worker_pid, NULL);
+        (void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
+        h->worker_pid = 0;
+        return 1;
+}
+
 static const char* const home_state_table[_HOME_STATE_MAX] = {
         [HOME_UNFIXATED]                   = "unfixated",
         [HOME_ABSENT]                      = "absent",
         [HOME_INACTIVE]                    = "inactive",
+        [HOME_DIRTY]                       = "dirty",
         [HOME_FIXATING]                    = "fixating",
         [HOME_FIXATING_FOR_ACTIVATION]     = "fixating-for-activation",
         [HOME_FIXATING_FOR_ACQUIRE]        = "fixating-for-acquire",