Backport of: From 221c26685354dea2b2732df94404e8e5e77a1591 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Wed, 10 Feb 2021 21:21:36 +0000 Subject: [PATCH 3/3] tests: Add tests for key name handling in the keyfile backend This tests the two recent commits. Signed-off-by: Philip Withnall Upstream-Status: Backport [https://mirrors.ocf.berkeley.edu/ubuntu/pool/main/g/glib2.0/glib2.0_2.64.6-1~ubuntu20.04.3.debian.tar.xz] CVE: CVE-2021-27219 Signed-off-by: Ranjitsinh Rathod --- gio/tests/gsettings.c | 170 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) --- a/gio/tests/gsettings.c +++ b/gio/tests/gsettings.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -1740,6 +1741,14 @@ key_changed_cb (GSettings *settings, con (*b) = TRUE; } +typedef struct +{ + const gchar *path; + const gchar *root_group; + const gchar *keyfile_group; + const gchar *root_path; +} KeyfileTestData; + /* * Test that using a keyfile works */ @@ -1834,7 +1843,11 @@ test_keyfile (Fixture *fixture, g_free (str); g_settings_set (settings, "farewell", "s", "cheerio"); - + + /* Check that empty keys/groups are not allowed. */ + g_assert_false (g_settings_is_writable (settings, "")); + g_assert_false (g_settings_is_writable (settings, "/")); + /* When executing as root, changing the mode of the keyfile will have * no effect on the writability of the settings. */ @@ -1866,6 +1879,149 @@ test_keyfile (Fixture *fixture, g_free (keyfile_path); } +/* + * Test that using a keyfile works with a schema with no path set. + */ +static void +test_keyfile_no_path (Fixture *fixture, + gconstpointer user_data) +{ + const KeyfileTestData *test_data = user_data; + GSettingsBackend *kf_backend; + GSettings *settings; + GKeyFile *keyfile; + gboolean writable; + gchar *key = NULL; + GError *error = NULL; + gchar *keyfile_path = NULL, *store_path = NULL; + + keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL); + store_path = g_build_filename (keyfile_path, "gsettings.store", NULL); + kf_backend = g_keyfile_settings_backend_new (store_path, test_data->root_path, test_data->root_group); + settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, test_data->path); + g_object_unref (kf_backend); + + g_settings_reset (settings, "test-boolean"); + g_assert_true (g_settings_get_boolean (settings, "test-boolean")); + + writable = g_settings_is_writable (settings, "test-boolean"); + g_assert_true (writable); + g_settings_set (settings, "test-boolean", "b", FALSE); + + g_assert_false (g_settings_get_boolean (settings, "test-boolean")); + + g_settings_delay (settings); + g_settings_set (settings, "test-boolean", "b", TRUE); + g_settings_apply (settings); + + keyfile = g_key_file_new (); + g_assert_true (g_key_file_load_from_file (keyfile, store_path, 0, NULL)); + + g_assert_true (g_key_file_get_boolean (keyfile, test_data->keyfile_group, "test-boolean", NULL)); + + g_key_file_free (keyfile); + + g_settings_reset (settings, "test-boolean"); + g_settings_apply (settings); + keyfile = g_key_file_new (); + g_assert_true (g_key_file_load_from_file (keyfile, store_path, 0, NULL)); + + g_assert_false (g_key_file_get_string (keyfile, test_data->keyfile_group, "test-boolean", &error)); + g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND); + g_clear_error (&error); + + /* Check that empty keys/groups are not allowed. */ + g_assert_false (g_settings_is_writable (settings, "")); + g_assert_false (g_settings_is_writable (settings, "/")); + + /* Keys which ghost the root group name are not allowed. This can only be + * tested when the path is `/` as otherwise it acts as a prefix and prevents + * any ghosting. */ + if (g_str_equal (test_data->path, "/")) + { + key = g_strdup_printf ("%s/%s", test_data->root_group, ""); + g_assert_false (g_settings_is_writable (settings, key)); + g_free (key); + + key = g_strdup_printf ("%s/%s", test_data->root_group, "/"); + g_assert_false (g_settings_is_writable (settings, key)); + g_free (key); + + key = g_strdup_printf ("%s/%s", test_data->root_group, "test-boolean"); + g_assert_false (g_settings_is_writable (settings, key)); + g_free (key); + } + + g_key_file_free (keyfile); + g_object_unref (settings); + + /* Clean up the temporary directory. */ + g_assert_cmpint (g_chmod (keyfile_path, 0777) == 0 ? 0 : errno, ==, 0); + g_assert_cmpint (g_remove (store_path) == 0 ? 0 : errno, ==, 0); + g_assert_cmpint (g_rmdir (keyfile_path) == 0 ? 0 : errno, ==, 0); + g_free (store_path); + g_free (keyfile_path); +} + +/* + * Test that a keyfile rejects writes to keys outside its root path. + */ +static void +test_keyfile_outside_root_path (Fixture *fixture, + gconstpointer user_data) +{ + GSettingsBackend *kf_backend; + GSettings *settings; + gchar *keyfile_path = NULL, *store_path = NULL; + + keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL); + store_path = g_build_filename (keyfile_path, "gsettings.store", NULL); + kf_backend = g_keyfile_settings_backend_new (store_path, "/tests/basic-types/", "root"); + settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, "/tests/"); + g_object_unref (kf_backend); + + g_assert_false (g_settings_is_writable (settings, "test-boolean")); + + g_object_unref (settings); + + /* Clean up the temporary directory. The keyfile probably doesn’t exist, so + * don’t error on failure. */ + g_remove (store_path); + g_assert_cmpint (g_rmdir (keyfile_path) == 0 ? 0 : errno, ==, 0); + g_free (store_path); + g_free (keyfile_path); +} + +/* + * Test that a keyfile rejects writes to keys in the root if no root group is set. + */ +static void +test_keyfile_no_root_group (Fixture *fixture, + gconstpointer user_data) +{ + GSettingsBackend *kf_backend; + GSettings *settings; + gchar *keyfile_path = NULL, *store_path = NULL; + + keyfile_path = g_build_filename (fixture->tmp_dir, "keyfile", NULL); + store_path = g_build_filename (keyfile_path, "gsettings.store", NULL); + kf_backend = g_keyfile_settings_backend_new (store_path, "/", NULL); + settings = g_settings_new_with_backend_and_path ("org.gtk.test.no-path", kf_backend, "/"); + g_object_unref (kf_backend); + + g_assert_false (g_settings_is_writable (settings, "test-boolean")); + g_assert_true (g_settings_is_writable (settings, "child/test-boolean")); + + g_object_unref (settings); + + /* Clean up the temporary directory. The keyfile probably doesn’t exist, so + * don’t error on failure. */ + g_remove (store_path); + g_assert_cmpint (g_rmdir (keyfile_path) == 0 ? 0 : errno, ==, 0); + g_free (store_path); + g_free (keyfile_path); +} + /* Test that getting child schemas works */ static void @@ -2844,6 +3000,14 @@ main (int argc, char *argv[]) gchar *override_text; gchar *enums; gint result; + const KeyfileTestData keyfile_test_data_explicit_path = { "/tests/", "root", "tests", "/" }; + const KeyfileTestData keyfile_test_data_empty_path = { "/", "root", "root", "/" }; + const KeyfileTestData keyfile_test_data_long_path = { + "/tests/path/is/very/long/and/this/makes/some/comparisons/take/a/different/branch/", + "root", + "tests/path/is/very/long/and/this/makes/some/comparisons/take/a/different/branch", + "/" + }; /* Meson build sets this */ #ifdef TEST_LOCALE_PATH @@ -2967,6 +3131,11 @@ main (int argc, char *argv[]) } g_test_add ("/gsettings/keyfile", Fixture, NULL, setup, test_keyfile, teardown); + g_test_add ("/gsettings/keyfile/explicit-path", Fixture, &keyfile_test_data_explicit_path, setup, test_keyfile_no_path, teardown); + g_test_add ("/gsettings/keyfile/empty-path", Fixture, &keyfile_test_data_empty_path, setup, test_keyfile_no_path, teardown); + g_test_add ("/gsettings/keyfile/long-path", Fixture, &keyfile_test_data_long_path, setup, test_keyfile_no_path, teardown); + g_test_add ("/gsettings/keyfile/outside-root-path", Fixture, NULL, setup, test_keyfile_outside_root_path, teardown); + g_test_add ("/gsettings/keyfile/no-root-group", Fixture, NULL, setup, test_keyfile_no_root_group, teardown); g_test_add_func ("/gsettings/child-schema", test_child_schema); g_test_add_func ("/gsettings/strinfo", test_strinfo); g_test_add_func ("/gsettings/enums", test_enums);