Skip to content

Commit cd67888

Browse files
mtwebsterclefebvre
authored andcommitted
comics: Use libarchive to unpack documents [CVE-2023-44452].
This commit eliminates the use of external commands for opening comic documents, and uses libarchive instead. Fixes: CVE-2023-44452 - Linux Mint Xreader CBT File Parsing Argument Injection Remote Code Execution Vulnerability. Based on: https://gitlab.gnome.org/GNOME/evince/-/commit/7b5ad18399b04cbfce02730d28baf30e9fc35b58 This vulnerability was discovered by: Febin Mon Saji working with Trend Micro Zero Day Initiative
1 parent 141f131 commit cd67888

10 files changed

+995
-790
lines changed

backend/comics/comics-document.c

+444-766
Large diffs are not rendered by default.

backend/comics/comics-document.h

+3-6
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1717
*/
1818

19-
#ifndef __COMICS_DOCUMENT_H__
20-
#define __COMICS_DOCUMENT_H__
19+
#pragma once
2120

21+
#include "ev-macros.h"
2222
#include "ev-document.h"
2323

2424
G_BEGIN_DECLS
@@ -30,9 +30,6 @@ G_BEGIN_DECLS
3030
typedef struct _ComicsDocument ComicsDocument;
3131

3232
GType comics_document_get_type (void) G_GNUC_CONST;
33+
GType register_evince_backend (GTypeModule *module);
3334

34-
G_MODULE_EXPORT GType register_xreader_backend (GTypeModule *module);
35-
3635
G_END_DECLS
37-
38-
#endif /* __COMICS_DOCUMENT_H__ */
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[Xreader Backend]
22
Module=comicsdocument
3-
_TypeDescription=Comic Books
4-
MimeType=application/x-cbr;application/x-cbz;application/x-cb7;application/x-cbt;application/vnd.comicbook+zip;
3+
TypeDescription=Comic Books
4+
MimeType=@COMICS_MIME_TYPES@;

backend/comics/ev-archive.c

+323
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2+
/*
3+
* Copyright (C) 2017, Bastien Nocera <[email protected]>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation; either version 2, or (at your option)
8+
* any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18+
*/
19+
20+
#include "config.h"
21+
#include "ev-archive.h"
22+
23+
#include <archive.h>
24+
#include <archive_entry.h>
25+
#include <gio/gio.h>
26+
27+
#define BUFFER_SIZE (64 * 1024)
28+
29+
struct _EvArchive {
30+
GObject parent_instance;
31+
EvArchiveType type;
32+
33+
/* libarchive */
34+
struct archive *libar;
35+
struct archive_entry *libar_entry;
36+
};
37+
38+
G_DEFINE_TYPE(EvArchive, ev_archive, G_TYPE_OBJECT);
39+
40+
static void
41+
ev_archive_finalize (GObject *object)
42+
{
43+
EvArchive *archive = EV_ARCHIVE (object);
44+
45+
switch (archive->type) {
46+
case EV_ARCHIVE_TYPE_RAR:
47+
case EV_ARCHIVE_TYPE_ZIP:
48+
case EV_ARCHIVE_TYPE_7Z:
49+
case EV_ARCHIVE_TYPE_TAR:
50+
g_clear_pointer (&archive->libar, archive_free);
51+
break;
52+
default:
53+
break;
54+
}
55+
56+
G_OBJECT_CLASS (ev_archive_parent_class)->finalize (object);
57+
}
58+
59+
static void
60+
ev_archive_class_init (EvArchiveClass *klass)
61+
{
62+
GObjectClass *object_class = (GObjectClass *) klass;
63+
64+
object_class->finalize = ev_archive_finalize;
65+
}
66+
67+
EvArchive *
68+
ev_archive_new (void)
69+
{
70+
return g_object_new (EV_TYPE_ARCHIVE, NULL);
71+
}
72+
73+
static void
74+
libarchive_set_archive_type (EvArchive *archive,
75+
EvArchiveType archive_type)
76+
{
77+
archive->type = archive_type;
78+
archive->libar = archive_read_new ();
79+
80+
if (archive_type == EV_ARCHIVE_TYPE_ZIP)
81+
archive_read_support_format_zip (archive->libar);
82+
else if (archive_type == EV_ARCHIVE_TYPE_7Z)
83+
archive_read_support_format_7zip (archive->libar);
84+
else if (archive_type == EV_ARCHIVE_TYPE_TAR)
85+
archive_read_support_format_tar (archive->libar);
86+
else if (archive_type == EV_ARCHIVE_TYPE_RAR) {
87+
archive_read_support_format_rar (archive->libar);
88+
archive_read_support_format_rar5 (archive->libar);
89+
} else
90+
g_assert_not_reached ();
91+
}
92+
93+
EvArchiveType
94+
ev_archive_get_archive_type (EvArchive *archive)
95+
{
96+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), EV_ARCHIVE_TYPE_NONE);
97+
98+
return archive->type;
99+
}
100+
101+
gboolean
102+
ev_archive_set_archive_type (EvArchive *archive,
103+
EvArchiveType archive_type)
104+
{
105+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
106+
g_return_val_if_fail (archive->type == EV_ARCHIVE_TYPE_NONE, FALSE);
107+
108+
switch (archive_type) {
109+
case EV_ARCHIVE_TYPE_RAR:
110+
case EV_ARCHIVE_TYPE_ZIP:
111+
case EV_ARCHIVE_TYPE_7Z:
112+
case EV_ARCHIVE_TYPE_TAR:
113+
libarchive_set_archive_type (archive, archive_type);
114+
break;
115+
default:
116+
g_assert_not_reached ();
117+
}
118+
119+
return TRUE;
120+
}
121+
122+
gboolean
123+
ev_archive_open_filename (EvArchive *archive,
124+
const char *path,
125+
GError **error)
126+
{
127+
int r;
128+
129+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
130+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
131+
g_return_val_if_fail (path != NULL, FALSE);
132+
133+
switch (archive->type) {
134+
case EV_ARCHIVE_TYPE_NONE:
135+
g_assert_not_reached ();
136+
case EV_ARCHIVE_TYPE_RAR:
137+
case EV_ARCHIVE_TYPE_ZIP:
138+
case EV_ARCHIVE_TYPE_7Z:
139+
case EV_ARCHIVE_TYPE_TAR:
140+
r = archive_read_open_filename (archive->libar, path, BUFFER_SIZE);
141+
if (r != ARCHIVE_OK) {
142+
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
143+
"Error opening archive: %s", archive_error_string (archive->libar));
144+
return FALSE;
145+
}
146+
return TRUE;
147+
}
148+
149+
return FALSE;
150+
}
151+
152+
static gboolean
153+
libarchive_read_next_header (EvArchive *archive,
154+
GError **error)
155+
{
156+
while (1) {
157+
int r;
158+
159+
r = archive_read_next_header (archive->libar, &archive->libar_entry);
160+
if (r != ARCHIVE_OK) {
161+
if (r != ARCHIVE_EOF)
162+
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
163+
"Error reading archive: %s", archive_error_string (archive->libar));
164+
return FALSE;
165+
}
166+
167+
if (archive_entry_filetype (archive->libar_entry) != AE_IFREG) {
168+
g_debug ("Skipping '%s' as it's not a regular file",
169+
archive_entry_pathname (archive->libar_entry));
170+
continue;
171+
}
172+
173+
g_debug ("At header for file '%s'", archive_entry_pathname (archive->libar_entry));
174+
175+
break;
176+
}
177+
178+
return TRUE;
179+
}
180+
181+
gboolean
182+
ev_archive_read_next_header (EvArchive *archive,
183+
GError **error)
184+
{
185+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
186+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
187+
188+
switch (archive->type) {
189+
case EV_ARCHIVE_TYPE_NONE:
190+
g_assert_not_reached ();
191+
case EV_ARCHIVE_TYPE_RAR:
192+
case EV_ARCHIVE_TYPE_ZIP:
193+
case EV_ARCHIVE_TYPE_7Z:
194+
case EV_ARCHIVE_TYPE_TAR:
195+
return libarchive_read_next_header (archive, error);
196+
}
197+
198+
return FALSE;
199+
}
200+
201+
gboolean
202+
ev_archive_at_entry (EvArchive *archive)
203+
{
204+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
205+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
206+
207+
return (archive->libar_entry != NULL);
208+
}
209+
210+
const char *
211+
ev_archive_get_entry_pathname (EvArchive *archive)
212+
{
213+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), NULL);
214+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, NULL);
215+
216+
switch (archive->type) {
217+
case EV_ARCHIVE_TYPE_NONE:
218+
g_assert_not_reached ();
219+
case EV_ARCHIVE_TYPE_RAR:
220+
case EV_ARCHIVE_TYPE_ZIP:
221+
case EV_ARCHIVE_TYPE_7Z:
222+
case EV_ARCHIVE_TYPE_TAR:
223+
g_return_val_if_fail (archive->libar_entry != NULL, NULL);
224+
return archive_entry_pathname (archive->libar_entry);
225+
}
226+
227+
return NULL;
228+
}
229+
230+
gint64
231+
ev_archive_get_entry_size (EvArchive *archive)
232+
{
233+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
234+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
235+
236+
switch (archive->type) {
237+
case EV_ARCHIVE_TYPE_NONE:
238+
g_assert_not_reached ();
239+
case EV_ARCHIVE_TYPE_RAR:
240+
case EV_ARCHIVE_TYPE_ZIP:
241+
case EV_ARCHIVE_TYPE_7Z:
242+
case EV_ARCHIVE_TYPE_TAR:
243+
g_return_val_if_fail (archive->libar_entry != NULL, -1);
244+
return archive_entry_size (archive->libar_entry);
245+
}
246+
247+
return -1;
248+
}
249+
250+
gboolean
251+
ev_archive_get_entry_is_encrypted (EvArchive *archive)
252+
{
253+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), FALSE);
254+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, FALSE);
255+
256+
switch (archive->type) {
257+
case EV_ARCHIVE_TYPE_NONE:
258+
g_assert_not_reached ();
259+
case EV_ARCHIVE_TYPE_RAR:
260+
case EV_ARCHIVE_TYPE_ZIP:
261+
case EV_ARCHIVE_TYPE_7Z:
262+
case EV_ARCHIVE_TYPE_TAR:
263+
g_return_val_if_fail (archive->libar_entry != NULL, -1);
264+
return archive_entry_is_encrypted (archive->libar_entry);
265+
}
266+
267+
return FALSE;
268+
}
269+
270+
gssize
271+
ev_archive_read_data (EvArchive *archive,
272+
void *buf,
273+
gsize count,
274+
GError **error)
275+
{
276+
gssize r = -1;
277+
278+
g_return_val_if_fail (EV_IS_ARCHIVE (archive), -1);
279+
g_return_val_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE, -1);
280+
281+
switch (archive->type) {
282+
case EV_ARCHIVE_TYPE_NONE:
283+
g_assert_not_reached ();
284+
case EV_ARCHIVE_TYPE_RAR:
285+
case EV_ARCHIVE_TYPE_ZIP:
286+
case EV_ARCHIVE_TYPE_7Z:
287+
case EV_ARCHIVE_TYPE_TAR:
288+
g_return_val_if_fail (archive->libar_entry != NULL, -1);
289+
r = archive_read_data (archive->libar, buf, count);
290+
if (r < 0) {
291+
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
292+
"Failed to decompress data: %s", archive_error_string (archive->libar));
293+
}
294+
break;
295+
}
296+
297+
return r;
298+
}
299+
300+
void
301+
ev_archive_reset (EvArchive *archive)
302+
{
303+
g_return_if_fail (EV_IS_ARCHIVE (archive));
304+
g_return_if_fail (archive->type != EV_ARCHIVE_TYPE_NONE);
305+
306+
switch (archive->type) {
307+
case EV_ARCHIVE_TYPE_RAR:
308+
case EV_ARCHIVE_TYPE_ZIP:
309+
case EV_ARCHIVE_TYPE_7Z:
310+
case EV_ARCHIVE_TYPE_TAR:
311+
g_clear_pointer (&archive->libar, archive_free);
312+
libarchive_set_archive_type (archive, archive->type);
313+
archive->libar_entry = NULL;
314+
break;
315+
default:
316+
g_assert_not_reached ();
317+
}
318+
}
319+
320+
static void
321+
ev_archive_init (EvArchive *archive)
322+
{
323+
}

0 commit comments

Comments
 (0)