return FALSE;
}
+static bool is_reserved_name(const subsession_user_t subsession)
+{
+ return !strcmp(subsession, "__template_fixed__")
+ || !strcmp(subsession, "__template_reg__");
+}
+
static gboolean error_on_bad_user_id(const char *user_id) {
return (
is_user_id_null(user_id)
user_id_is_not_valid(user))
)
+ if (is_reserved_name(user))
+ return SUBSESSION_ERROR_INVALID_PARAMETER;
+
return_with_log_error_result_(method_call_async(dbus_method_call.AddUser, g_variant_new("(is)", session_uid, user), cb, user_data))
}
session_fixed_size_invalid_size(size_limit_kB)))
)
+ if (is_reserved_name(user))
+ return SUBSESSION_ERROR_INVALID_PARAMETER;
+
return_with_log_error_result_(method_call_async(dbus_method_call.AddUserFixedSize, g_variant_new("(isu)", session_uid, user, size_limit_kB), cb, user_data))
}
user_id_is_not_valid(user))
)
+ if (is_reserved_name(user))
+ return SUBSESSION_ERROR_INVALID_PARAMETER;
+
return_with_log_error_result_(method_call_async(dbus_method_call.RemoveUser, g_variant_new("(is)", session_uid, user), cb, user_data))
}
session_uid_is_not_valid(session_uid))
)
+ if (is_reserved_name(next_user))
+ return SUBSESSION_ERROR_INVALID_PARAMETER;
+
return_with_log_error_result_(method_call_async(dbus_method_call.SwitchUser, g_variant_new("(is)", session_uid, next_user), cb, user_data))
}
#pragma once
#include <filesystem>
+#include <string_view>
namespace fs = std::filesystem;
virtual fs::path AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const = 0;
virtual void AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path, int uid, int gid) const = 0;
virtual void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const = 0;
+ virtual std::string_view TemplateName() const = 0;
};
// Should be unique per-backend.
static const std::string TMP_NEW_PREFIX = ".tmpfixednew";
+std::string_view DirBackendAddFixedSize::TemplateName() const
+{
+ return "__template_fixed__"sv;
+}
+
fs::path DirBackendFixedSize::GetImagePathFromSubsessionPath(fs::path subsession_path)
{
subsession_path.replace_filename(".img."s + subsession_path.filename().native());
fs::path AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const override;
void AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path, int uid, int gid) const override;
void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const override;
+ std::string_view TemplateName() const override;
};
#include "dir_backend_regular_dir.hpp"
#include "os_ops.hpp"
-using namespace std::string_literals;
+using namespace std::literals;
+
+std::string_view DirBackendAddRegularDir::TemplateName() const
+{
+ /* Still has to be a valid subsession name (to be visible
+ * to external update scripts) so must fit in 20 chars. */
+ return "__template_reg__"sv;
+}
fs::path DirBackendAddRegularDir::AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const
{
fs::path AddSubsessionPrepare (const fs::path& subsession_path, int uid, int gid) const override;
void AddSubsessionFinalize (const fs::path& tmpdir_path, const fs::path& subsession_path, int uid, int gid) const override;
void AddSubsessionCleanupFailure (const fs::path& tmpdir_path, const fs::path& subsession_path) const override;
+ std::string_view TemplateName() const override;
};
}
// Create `$HOME/subsession` directory if it doesn't exist
-static void create_main_subdirectory(const int session_uid, const fs::path& main_dir)
+void create_main_subdirectory(const int session_uid, const fs::path& main_dir)
{
if (fs::exists(main_dir))
return;
std::filesystem::path get_main_dir_by_user_id(const int uid);
std::filesystem::path get_last_subsession_path_by_user_id(const int uid);
+void create_main_subdirectory(const int session_uid, const fs::path& main_dir);
bool subsession_exists(const int session_uid, const std::string_view subsession_id);
void add_user_subsession(const int session_uid, const std::string_view subsession_id, const DirBackendAdd& backend);
void add_user_subsession_inner(const std::filesystem::path &main_path, const int session_uid, const std::string_view subsession_id, const DirBackendAdd& backend, bool force);
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. */
+#include "dir_backend_fixed_size.hpp"
+#include "dir_backend_regular_dir.hpp"
+
#include <chrono>
#include <exception>
#include <stdexcept>
return true;
}
+inline bool check_subsession_id_restricted(const std::string_view subsession_id)
+{
+ return DirBackendAddFixedSize{0}.TemplateName() == subsession_id
+ || DirBackendAddRegularDir{}.TemplateName() == subsession_id;
+}
+
inline bool check_uid_valid(int uid)
{
return uid > 0;
int main(int argc, char **argv) try {
if (isRestoreOnly(argc, argv)) {
- restore_all_user_sessions();
+ restore_all_user_sessions(true);
} else {
g_sessiond_context = std::make_unique <sessiond_context> ();
- restore_all_user_sessions();
+ restore_all_user_sessions(false);
g_sessiond_context->run();
}
} catch (const std::exception &ex) {
return true;
}
+ if (check_subsession_id_restricted(subsession_id)) {
+ g_dbus_method_invocation_return_dbus_error(invocation,
+ get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Reserved subsession_id");
+ return true;
+ }
+
return false;
}
}
// N.B. Switch to user "" (empty string) is possible and it means no subsession is currently active
- if (next_subsession_id != SUBSESSION_INITIAL_SID && !check_subsession_id_valid(next_subsession_id)) {
+ if (next_subsession_id != SUBSESSION_INITIAL_SID && (!check_subsession_id_valid(next_subsession_id) || check_subsession_id_restricted(next_subsession_id))) {
g_dbus_method_invocation_return_dbus_error(invocation,
get_dbus_error_mapping(SUBSESSION_ERROR_INVALID_PARAMETER), "Incorrect subsession_id passed");
return;
using namespace std::string_literals;
+static constexpr auto TEMPLATE_IMAGE_SIZE_KB = 10240;
+
std::string get_restoration_lock_filename(int uid)
{
return "/run/sessiond-restore-"s + std::to_string(uid) + ".lock";
throw;
}
-void restore_all_user_sessions()
+static void regenerate_template_sessions(int uid) try
+{
+ const fs::path main_path = get_main_dir_by_user_id(uid);
+ create_main_subdirectory(uid, main_path);
+
+ const auto add_template_for_backend = [&] (const DirBackendAdd &backend) {
+ /* Don't regenerate templates and assume that whatever was left from
+ * a previous boot, if anything, is correct. Image-backed subsessions
+ * are remounted early on (including via an early `--restore-only`). */
+ if (subsession_exists(uid, backend.TemplateName()))
+ return;
+ add_user_subsession_inner(main_path, uid, backend.TemplateName(), backend, false);
+ };
+
+ add_template_for_backend(DirBackendAddFixedSize {TEMPLATE_IMAGE_SIZE_KB});
+ add_template_for_backend(DirBackendAddRegularDir {});
+} catch (const std::exception &ex) {
+ LOGE("Exception while regenerating template subsessions for uid %d: %s", uid, ex.what());
+ throw;
+}
+
+void restore_all_user_sessions(bool restore_only)
{
/* In theory this should live among OS or FS helpers, but
* this happens at early boot so we care about performance
LOGI("Restoring last session for user %s (uid %d)", username.c_str(), uid);
restore_user_session(username, uid);
+
+ if (!restore_only) {
+ /* Templates aren't supposed to be used directly,
+ * and we always get a "fresh" copy when regenerated,
+ * so we don't need to create them at this point and
+ * can save up on some boot-time performance. */
+ LOGI("Regenerating template sessions for user %s (uid %d)", username.c_str(), uid);
+ regenerate_template_sessions(uid);
+ }
}
}
#pragma once
-void restore_all_user_sessions();
+void restore_all_user_sessions(bool only_restore);
#include "test_hlp.hpp"
-const int action_items = 44;
+const int action_items = 50;
TEST(subsession_add_remove_test, FailAtAddRemoveUser) {
using ud_t = ud_data_t<std::array<api_call_res_t, action_items>>;
ud_t ud_data = { .loop = g_main_loop_new(NULL, FALSE),
.results = std::array<api_call_res_t, action_items> {
+ // Cannot add templates names (2 reserved names x 2 request backends, both names block both backends)
+ api_call_res_t{ .call_result = std::move(subsession_add_user_l<subsession_5001>( TestBadUserStr::fixed_size_template)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if add [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error (regular dir backend)",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if add [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error (regular dir backend)", },
+
+ api_call_res_t{ .call_result = std::move(subsession_add_user_l<subsession_5001>( TestBadUserStr::regular_dir_template)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if add [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error (regular dir backend)",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if add [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error (regular dir backend)", },
+
+
+ api_call_res_t{ .call_result = std::move(subsession_add_user_fixed_size_l<subsession_5001>( TestBadUserStr::fixed_size_template, 10*1024)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if add [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error (fixed size backend)",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if add [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error (fixed size backend)", },
+
+ api_call_res_t{ .call_result = std::move(subsession_add_user_fixed_size_l<subsession_5001>( TestBadUserStr::regular_dir_template, 10*1024)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if add [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error (fixed size backend)",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if add [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error (fixed size backend)", },
+
+ // Cannot remove templates either
+ api_call_res_t{ .call_result = std::move(subsession_remove_user_l<subsession_5001>( TestBadUserStr::regular_dir_template)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if remove [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if remove [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error", },
+ api_call_res_t{ .call_result = std::move(subsession_remove_user_l<subsession_5001>(TestBadUserStr::fixed_size_template)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if remove [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if remove [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error", },
+
+
+ // Misc
api_call_res_t{ .call_result = std::move(subsession_add_user_l<subsession_5001>( TestBadUserStr::bad_user_1)),
.expected = SUBSESSION_ERROR_INVALID_PARAMETER,
.desc = "Check if add [ " + std::string(TestBadUserStr::bad_user_1) + " ] returns error (regular dir backend)",
loop_run_for_test(callback_pending_ud<ud_get_user_t>,(gpointer*)&ud, ud.loop);
- summarize_results_for_get_user_list(std::get<get_user::get_user_list_0>(ud.t), std::array<std::string, 1>({""}));
- std::array<std::string, 3> arr{std::string(TestUserStr::user_0), std::string(TestUserStr::user_1), std::string(TestUserStr::user_2)};
-
- summarize_results_for_get_user_list<std::array<std::string, 3>>(std::get<get_user::get_user_list_1_2>(ud.t), arr);
- summarize_results_for_get_user_list<std::array<std::string, 3>>(std::get<get_user::get_user_list_mixed_1_2>(ud.t), arr);
- summarize_results_for_get_user_list<std::array<std::string, 3>>(std::get<get_user::get_user_list_fixed_size_1_2>(ud.t), arr);
- summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_1_2_count_only>(ud.t), 3);
- summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_mixed_1_2_count_only>(ud.t), 3);
- summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_fixed_size_1_2_count_only>(ud.t), 3);
+ summarize_results_for_get_user_list(std::get<get_user::get_user_list_0>(ud.t), std::array<std::string, 3>({"", "__template_fixed__", "__template_reg__"}));
+ std::array<std::string, 5> arr{std::string(TestUserStr::user_0), std::string(TestUserStr::user_1), std::string(TestUserStr::user_2)
+ , "__template_fixed__", "__template_reg__"};
+
+ summarize_results_for_get_user_list<std::array<std::string, 5>>(std::get<get_user::get_user_list_1_2>(ud.t), arr);
+ summarize_results_for_get_user_list<std::array<std::string, 5>>(std::get<get_user::get_user_list_mixed_1_2>(ud.t), arr);
+ summarize_results_for_get_user_list<std::array<std::string, 5>>(std::get<get_user::get_user_list_fixed_size_1_2>(ud.t), arr);
+ summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_1_2_count_only>(ud.t), 5);
+ summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_mixed_1_2_count_only>(ud.t), 5);
+ summarize_results_for_counted_only_get_user_list(std::get<get_user::get_user_list_fixed_size_1_2_count_only>(ud.t), 5);
summarize_results<get_user::action_items>(results);
}
#include "test_hlp.hpp"
-const int action_items = 12;
+const int action_items = 14;
TEST(subsession_switch_user_test, FailAtSwitchUser) {
.desc = "Check if switch to [ " + std::string(TestUserStr::user_2) + " ] returns no error",
.cb_expected = SUBSESSION_ERROR_NONE,
.cb_desc = "Check if switch to [" + std::string(TestUserStr::user_2) + " ] callback returns no error", },
+
+ // Cannot switch into templates
+ api_call_res_t{ .call_result = std::move(subsession_switch_user_l<subsession_5001>( TestBadUserStr::fixed_size_template)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if switch to template [ " + std::string(TestBadUserStr::fixed_size_template) + " ] returns error",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if switch to template [ " + std::string(TestBadUserStr::fixed_size_template) + " ] callback returns error", },
+
+ api_call_res_t{ .call_result = std::move(subsession_switch_user_l<subsession_5001>( TestBadUserStr::regular_dir_template)),
+ .expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .desc = "Check if switch to template [ " + std::string(TestBadUserStr::regular_dir_template) + " ] returns error",
+ .cb_expected = SUBSESSION_ERROR_INVALID_PARAMETER,
+ .cb_desc = "Check if switch to template [ " + std::string(TestBadUserStr::regular_dir_template) + " ] callback returns error", },
}
};
//Below filled subsession_user_t array without '\0' (null) terminator, use only for testing.
[[maybe_unused]] static subsession_user_t bad_user_20 = {'1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6',
'7','8','9','0'};
+
+ [[maybe_unused]] static subsession_user_t fixed_size_template = "__template_fixed__";
+ [[maybe_unused]] static subsession_user_t regular_dir_template = "__template_reg__";
};
static constexpr int MAGIC_ADD = 111;