diff options
Diffstat (limited to 'meta/recipes-core/glib-2.0/glib-2.0/CVE-2021-28153-4.patch')
-rw-r--r-- | meta/recipes-core/glib-2.0/glib-2.0/CVE-2021-28153-4.patch | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2021-28153-4.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2021-28153-4.patch new file mode 100644 index 0000000000..5b106e8474 --- /dev/null +++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2021-28153-4.patch | |||
@@ -0,0 +1,265 @@ | |||
1 | Backport of: | ||
2 | |||
3 | From 317b3b587058a05dca95d56dac26568c5b098d33 Mon Sep 17 00:00:00 2001 | ||
4 | From: Philip Withnall <pwithnall@endlessos.org> | ||
5 | Date: Wed, 24 Feb 2021 17:36:07 +0000 | ||
6 | Subject: [PATCH 4/5] glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION | ||
7 | with symlinks | ||
8 | MIME-Version: 1.0 | ||
9 | Content-Type: text/plain; charset=UTF-8 | ||
10 | Content-Transfer-Encoding: 8bit | ||
11 | |||
12 | The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking | ||
13 | the destination file and re-creating it from scratch. That did | ||
14 | previously work, but in the process the code would call `open(O_CREAT)` | ||
15 | on the file. If the file was a dangling symlink, this would create the | ||
16 | destination file (empty). That’s not an intended side-effect, and has | ||
17 | security implications if the symlink is controlled by a lower-privileged | ||
18 | process. | ||
19 | |||
20 | Fix that by not opening the destination file if it’s a symlink, and | ||
21 | adjusting the rest of the code to cope with | ||
22 | - the fact that `fd == -1` is not an error iff `is_symlink` is true, | ||
23 | - and that `original_stat` will contain the `lstat()` results for the | ||
24 | symlink now, rather than the `stat()` results for its target (again, | ||
25 | iff `is_symlink` is true). | ||
26 | |||
27 | This means that the target of the dangling symlink is no longer created, | ||
28 | which was the bug. The symlink itself continues to be replaced (as | ||
29 | before) with the new file — this is the intended behaviour of | ||
30 | `g_file_replace()`. | ||
31 | |||
32 | The behaviour for non-symlink cases, or cases where the symlink was not | ||
33 | dangling, should be unchanged. | ||
34 | |||
35 | Includes a unit test. | ||
36 | |||
37 | Signed-off-by: Philip Withnall <pwithnall@endlessos.org> | ||
38 | |||
39 | Fixes: #2325 | ||
40 | |||
41 | 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] | ||
42 | CVE: CVE-2021-28153 | ||
43 | Signed-off-by: Neetika Singh <Neetika.Singh@kpit.com> | ||
44 | Signed-off-by: Ranjitsinh Rathod <ranjitsinh.rathod@kpit.com> | ||
45 | |||
46 | --- | ||
47 | gio/glocalfileoutputstream.c | 77 ++++++++++++++++++------- | ||
48 | gio/tests/file.c | 108 +++++++++++++++++++++++++++++++++++ | ||
49 | 2 files changed, 163 insertions(+), 22 deletions(-) | ||
50 | |||
51 | --- a/gio/glocalfileoutputstream.c | ||
52 | +++ b/gio/glocalfileoutputstream.c | ||
53 | @@ -875,16 +875,22 @@ handle_overwrite_open (const char *fi | ||
54 | /* Could be a symlink, or it could be a regular ELOOP error, | ||
55 | * but then the next open will fail too. */ | ||
56 | is_symlink = TRUE; | ||
57 | - fd = g_open (filename, open_flags, mode); | ||
58 | + if (!replace_destination_set) | ||
59 | + fd = g_open (filename, open_flags, mode); | ||
60 | } | ||
61 | -#else | ||
62 | - fd = g_open (filename, open_flags, mode); | ||
63 | - errsv = errno; | ||
64 | +#else /* if !O_NOFOLLOW */ | ||
65 | /* This is racy, but we do it as soon as possible to minimize the race */ | ||
66 | is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK); | ||
67 | + | ||
68 | + if (!is_symlink || !replace_destination_set) | ||
69 | + { | ||
70 | + fd = g_open (filename, open_flags, mode); | ||
71 | + errsv = errno; | ||
72 | + } | ||
73 | #endif | ||
74 | |||
75 | - if (fd == -1) | ||
76 | + if (fd == -1 && | ||
77 | + (!is_symlink || !replace_destination_set)) | ||
78 | { | ||
79 | char *display_name = g_filename_display_name (filename); | ||
80 | g_set_error (error, G_IO_ERROR, | ||
81 | @@ -898,7 +904,14 @@ handle_overwrite_open (const char *fi | ||
82 | #ifdef G_OS_WIN32 | ||
83 | res = GLIB_PRIVATE_CALL (g_win32_fstat) (fd, &original_stat); | ||
84 | #else | ||
85 | - res = fstat (fd, &original_stat); | ||
86 | + if (!is_symlink) | ||
87 | + { | ||
88 | + res = fstat (fd, &original_stat); | ||
89 | + } | ||
90 | + else | ||
91 | + { | ||
92 | + res = lstat (filename, &original_stat); | ||
93 | + } | ||
94 | #endif | ||
95 | errsv = errno; | ||
96 | |||
97 | @@ -917,16 +930,27 @@ handle_overwrite_open (const char *fi | ||
98 | if (!S_ISREG (original_stat.st_mode)) | ||
99 | { | ||
100 | if (S_ISDIR (original_stat.st_mode)) | ||
101 | - g_set_error_literal (error, | ||
102 | - G_IO_ERROR, | ||
103 | - G_IO_ERROR_IS_DIRECTORY, | ||
104 | - _("Target file is a directory")); | ||
105 | - else | ||
106 | - g_set_error_literal (error, | ||
107 | + { | ||
108 | + g_set_error_literal (error, | ||
109 | + G_IO_ERROR, | ||
110 | + G_IO_ERROR_IS_DIRECTORY, | ||
111 | + _("Target file is a directory")); | ||
112 | + goto err_out; | ||
113 | + } | ||
114 | + else if (!is_symlink || | ||
115 | +#ifdef S_ISLNK | ||
116 | + !S_ISLNK (original_stat.st_mode) | ||
117 | +#else | ||
118 | + FALSE | ||
119 | +#endif | ||
120 | + ) | ||
121 | + { | ||
122 | + g_set_error_literal (error, | ||
123 | G_IO_ERROR, | ||
124 | G_IO_ERROR_NOT_REGULAR_FILE, | ||
125 | _("Target file is not a regular file")); | ||
126 | - goto err_out; | ||
127 | + goto err_out; | ||
128 | + } | ||
129 | } | ||
130 | |||
131 | if (etag != NULL) | ||
132 | @@ -1007,7 +1031,8 @@ handle_overwrite_open (const char *fi | ||
133 | } | ||
134 | } | ||
135 | |||
136 | - g_close (fd, NULL); | ||
137 | + if (fd >= 0) | ||
138 | + g_close (fd, NULL); | ||
139 | *temp_filename = tmp_filename; | ||
140 | return tmpfd; | ||
141 | } | ||
142 | --- a/gio/tests/file.c | ||
143 | +++ b/gio/tests/file.c | ||
144 | @@ -804,6 +804,113 @@ test_replace_cancel (void) | ||
145 | g_object_unref (tmpdir); | ||
146 | } | ||
147 | |||
148 | +static void | ||
149 | +test_replace_symlink (void) | ||
150 | +{ | ||
151 | +#ifdef G_OS_UNIX | ||
152 | + gchar *tmpdir_path = NULL; | ||
153 | + GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL; | ||
154 | + GFileOutputStream *stream = NULL; | ||
155 | + const gchar *new_contents = "this is a test message which should be written to source and not target"; | ||
156 | + gsize n_written; | ||
157 | + GFileEnumerator *enumerator = NULL; | ||
158 | + GFileInfo *info = NULL; | ||
159 | + gchar *contents = NULL; | ||
160 | + gsize length = 0; | ||
161 | + GError *local_error = NULL; | ||
162 | + | ||
163 | + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325"); | ||
164 | + g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks"); | ||
165 | + | ||
166 | + /* Create a fresh, empty working directory. */ | ||
167 | + tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error); | ||
168 | + g_assert_no_error (local_error); | ||
169 | + tmpdir = g_file_new_for_path (tmpdir_path); | ||
170 | + | ||
171 | + g_test_message ("Using temporary directory %s", tmpdir_path); | ||
172 | + g_free (tmpdir_path); | ||
173 | + | ||
174 | + /* Create symlink `source` which points to `target`. */ | ||
175 | + source_file = g_file_get_child (tmpdir, "source"); | ||
176 | + target_file = g_file_get_child (tmpdir, "target"); | ||
177 | + g_file_make_symbolic_link (source_file, "target", NULL, &local_error); | ||
178 | + g_assert_no_error (local_error); | ||
179 | + | ||
180 | + /* Ensure that `target` doesn’t exist */ | ||
181 | + g_assert_false (g_file_query_exists (target_file, NULL)); | ||
182 | + | ||
183 | + /* Replace the `source` symlink with a regular file using | ||
184 | + * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without* | ||
185 | + * following the symlink */ | ||
186 | + stream = g_file_replace (source_file, NULL, FALSE /* no backup */, | ||
187 | + G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error); | ||
188 | + g_assert_no_error (local_error); | ||
189 | + | ||
190 | + g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents), | ||
191 | + &n_written, NULL, &local_error); | ||
192 | + g_assert_no_error (local_error); | ||
193 | + g_assert_cmpint (n_written, ==, strlen (new_contents)); | ||
194 | + | ||
195 | + g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error); | ||
196 | + g_assert_no_error (local_error); | ||
197 | + | ||
198 | + g_clear_object (&stream); | ||
199 | + | ||
200 | + /* At this point, there should still only be one file: `source`. It should | ||
201 | + * now be a regular file. `target` should not exist. */ | ||
202 | + enumerator = g_file_enumerate_children (tmpdir, | ||
203 | + G_FILE_ATTRIBUTE_STANDARD_NAME "," | ||
204 | + G_FILE_ATTRIBUTE_STANDARD_TYPE, | ||
205 | + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error); | ||
206 | + g_assert_no_error (local_error); | ||
207 | + | ||
208 | + info = g_file_enumerator_next_file (enumerator, NULL, &local_error); | ||
209 | + g_assert_no_error (local_error); | ||
210 | + g_assert_nonnull (info); | ||
211 | + | ||
212 | + g_assert_cmpstr (g_file_info_get_name (info), ==, "source"); | ||
213 | + g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR); | ||
214 | + | ||
215 | + g_clear_object (&info); | ||
216 | + | ||
217 | + info = g_file_enumerator_next_file (enumerator, NULL, &local_error); | ||
218 | + g_assert_no_error (local_error); | ||
219 | + g_assert_null (info); | ||
220 | + | ||
221 | + g_file_enumerator_close (enumerator, NULL, &local_error); | ||
222 | + g_assert_no_error (local_error); | ||
223 | + g_clear_object (&enumerator); | ||
224 | + | ||
225 | + /* Double-check that `target` doesn’t exist */ | ||
226 | + g_assert_false (g_file_query_exists (target_file, NULL)); | ||
227 | + | ||
228 | + /* Check the content of `source`. */ | ||
229 | + g_file_load_contents (source_file, | ||
230 | + NULL, | ||
231 | + &contents, | ||
232 | + &length, | ||
233 | + NULL, | ||
234 | + &local_error); | ||
235 | + g_assert_no_error (local_error); | ||
236 | + g_assert_cmpstr (contents, ==, new_contents); | ||
237 | + g_assert_cmpuint (length, ==, strlen (new_contents)); | ||
238 | + g_free (contents); | ||
239 | + | ||
240 | + /* Tidy up. */ | ||
241 | + g_file_delete (source_file, NULL, &local_error); | ||
242 | + g_assert_no_error (local_error); | ||
243 | + | ||
244 | + g_file_delete (tmpdir, NULL, &local_error); | ||
245 | + g_assert_no_error (local_error); | ||
246 | + | ||
247 | + g_clear_object (&target_file); | ||
248 | + g_clear_object (&source_file); | ||
249 | + g_clear_object (&tmpdir); | ||
250 | +#else /* if !G_OS_UNIX */ | ||
251 | + g_test_skip ("Symlink replacement tests can only be run on Unix") | ||
252 | +#endif | ||
253 | +} | ||
254 | + | ||
255 | static void | ||
256 | on_file_deleted (GObject *object, | ||
257 | GAsyncResult *result, | ||
258 | @@ -1752,6 +1859,7 @@ main (int argc, char *argv[]) | ||
259 | g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete); | ||
260 | g_test_add_func ("/file/replace-load", test_replace_load); | ||
261 | g_test_add_func ("/file/replace-cancel", test_replace_cancel); | ||
262 | + g_test_add_func ("/file/replace-symlink", test_replace_symlink); | ||
263 | g_test_add_func ("/file/async-delete", test_async_delete); | ||
264 | #ifdef G_OS_UNIX | ||
265 | g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode); | ||