/[smecontribs]/rpms/libarchive/contribs9/libarchive-2.8.3-CVE-2016-5418.patch
ViewVC logotype

Contents of /rpms/libarchive/contribs9/libarchive-2.8.3-CVE-2016-5418.patch

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download)
Sun Apr 8 03:27:37 2018 UTC (6 years, 7 months ago) by jpp
Branch: MAIN
CVS Tags: libarchive-2_8_5-1_el6_sme, libarchive-2_8_5-0_el6_sme, HEAD
sources

1 diff --git a/libarchive/archive_write_disk.c b/libarchive/archive_write_disk.c
2 index f61ae17..2acf847 100644
3 --- a/libarchive/archive_write_disk.c
4 +++ b/libarchive/archive_write_disk.c
5 @@ -218,6 +218,7 @@ struct archive_write_disk {
6 #define MINIMUM_DIR_MODE 0700
7 #define MAXIMUM_DIR_MODE 0775
8
9 +static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags);
10 static int check_symlinks(struct archive_write_disk *);
11 static int create_filesystem_object(struct archive_write_disk *);
12 static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname);
13 @@ -876,7 +877,7 @@ edit_deep_directories(struct archive_write_disk *a)
14 a->restore_pwd = -1;
15
16 /* If path is short, avoid the open() below. */
17 - if (strlen(tail) <= PATH_MAX)
18 + if (strlen(tail) < PATH_MAX)
19 return;
20
21 /* Try to record our starting dir. */
22 @@ -885,7 +886,7 @@ edit_deep_directories(struct archive_write_disk *a)
23 return;
24
25 /* As long as the path is too long... */
26 - while (strlen(tail) > PATH_MAX) {
27 + while (strlen(tail) >= PATH_MAX) {
28 /* Locate a dir prefix shorter than PATH_MAX. */
29 tail += PATH_MAX - 8;
30 while (tail > a->name && *tail != '/')
31 @@ -1081,6 +1082,10 @@ create_filesystem_object(struct archive_write_disk *a)
32 const char *linkname;
33 mode_t final_mode, mode;
34 int r;
35 + /* these for check_path_for_symlinks */
36 + char *linkname_copy; /* non-const copy of linkname */
37 + struct archive_string error_string;
38 + int error_number;
39
40 /* We identify hard/symlinks according to the link names. */
41 /* Since link(2) and symlink(2) don't handle modes, we're done here. */
42 @@ -1089,6 +1094,18 @@ create_filesystem_object(struct archive_write_disk *a)
43 #if !HAVE_LINK
44 return (EPERM);
45 #else
46 + archive_string_init(&error_string);
47 + linkname_copy = strdup(linkname);
48 + if (linkname_copy == NULL) {
49 + return (EPERM);
50 + }
51 + r = check_path_for_symlinks(linkname_copy, &error_number, &error_string, a->flags);
52 + free(linkname_copy);
53 + if (r != ARCHIVE_OK) {
54 + archive_set_error(&a->archive, error_number, "%s", error_string.s);
55 + /* EPERM is more appropriate than error_number for our callers */
56 + return (EPERM);
57 + }
58 r = link(linkname, a->name) ? errno : 0;
59 /*
60 * New cpio and pax formats allow hardlink entries
61 @@ -1421,97 +1438,213 @@ current_fixup(struct archive_write_disk *a, const char *pathname)
62 * recent paths.
63 */
64 /* TODO: Extend this to support symlinks on Windows Vista and later. */
65 -static int
66 -check_symlinks(struct archive_write_disk *a)
67 +
68 +/*
69 + * Checks the given path to see if any elements along it are symlinks. Returns
70 + * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
71 + */
72 +static int check_path_for_symlinks(char *path, int *error_number, struct archive_string *error_string, int flags)
73 {
74 #if !defined(HAVE_LSTAT)
75 /* Platform doesn't have lstat, so we can't look for symlinks. */
76 - (void)a; /* UNUSED */
77 + (void)path; /* UNUSED */
78 + (void)error_number; /* UNUSED */
79 + (void)error_string; /* UNUSED */
80 + (void)flags; /* UNUSED */
81 return (ARCHIVE_OK);
82 #else
83 - char *pn, *p;
84 + int res = ARCHIVE_OK;
85 + char *tail;
86 + char *head;
87 + int last;
88 char c;
89 int r;
90 struct stat st;
91 + int restore_pwd;
92 +
93 + /* Nothing to do here if name is empty */
94 + if(path[0] == '\0')
95 + return (ARCHIVE_OK);
96
97 /*
98 * Guard against symlink tricks. Reject any archive entry whose
99 * destination would be altered by a symlink.
100 + *
101 + * Walk the filename in chunks separated by '/'. For each segment:
102 + * - if it doesn't exist, continue
103 + * - if it's symlink, abort or remove it
104 + * - if it's a directory and it's not the last chunk, cd into it
105 + * As we go:
106 + * head points to the current (relative) path
107 + * tail points to the temporary \0 terminating the segment we're currently examining
108 + * c holds what used to be in *tail
109 + * last is 1 if this is the last tail
110 */
111 - /* Whatever we checked last time doesn't need to be re-checked. */
112 - pn = a->name;
113 - p = a->path_safe.s;
114 - while ((*pn != '\0') && (*p == *pn))
115 - ++p, ++pn;
116 - c = pn[0];
117 - /* Keep going until we've checked the entire name. */
118 - while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) {
119 + restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
120 + if (restore_pwd < 0)
121 + return (ARCHIVE_FATAL);
122 + head = path;
123 + tail = path;
124 + last = 0;
125 +
126 + /* TODO: reintroduce a safe cache here? */
127 +
128 + /* Keep going until we've checked the entire name.
129 + * head, tail, path all alias the same string, which is
130 + * temporarily zeroed at tail, so be careful restoring the
131 + * stashed (c=tail[0]) for error messages.
132 + * Exiting the loop with break is okay; continue is not.
133 + */
134 + while (!last) {
135 + /* Skip the separator we just consumed, plus any adjacent ones */
136 + while (*tail == '/')
137 + ++tail;
138 /* Skip the next path element. */
139 - while (*pn != '\0' && *pn != '/')
140 - ++pn;
141 - c = pn[0];
142 - pn[0] = '\0';
143 + while (*tail != '\0' && *tail != '/')
144 + ++tail;
145 + /* is this the last path component? */
146 + last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');
147 + /* temporarily truncate the string here */
148 + c = tail[0];
149 + tail[0] = '\0';
150 /* Check that we haven't hit a symlink. */
151 - r = lstat(a->name, &st);
152 + r = lstat(head, &st);
153 if (r != 0) {
154 + tail[0] = c;
155 /* We've hit a dir that doesn't exist; stop now. */
156 if (errno == ENOENT)
157 break;
158 + /* Treat any other error as fatal - best to be paranoid here */
159 + if(error_number) *error_number = errno;
160 + if(error_string)
161 + archive_string_sprintf(error_string,
162 + "Could not stat %s",
163 + path);
164 + res = (ARCHIVE_FATAL);
165 + break;
166 + } else if (S_ISDIR(st.st_mode)) {
167 + if (!last) {
168 + if (chdir(head) != 0) {
169 + tail[0] = c;
170 + if(error_number) *error_number = errno;
171 + if(error_string)
172 + archive_string_sprintf(error_string,
173 + "Could not chdir %s",
174 + path);
175 + res = (ARCHIVE_FATAL);
176 + break;
177 + }
178 + /* Our view is now from inside this dir: */
179 + head = tail + 1;
180 + }
181 } else if (S_ISLNK(st.st_mode)) {
182 - if (c == '\0') {
183 + if (last) {
184 /*
185 * Last element is symlink; remove it
186 * so we can overwrite it with the
187 * item being extracted.
188 */
189 - if (unlink(a->name)) {
190 - archive_set_error(&a->archive, errno,
191 - "Could not remove symlink %s",
192 - a->name);
193 - pn[0] = c;
194 - return (ARCHIVE_FAILED);
195 + if (unlink(head)) {
196 + tail[0] = c;
197 + if(error_number) *error_number = errno;
198 + if(error_string)
199 + archive_string_sprintf(error_string,
200 + "Could not remove symlink %s",
201 + path);
202 + res = (ARCHIVE_FAILED);
203 + break;
204 }
205 - a->pst = NULL;
206 /*
207 * Even if we did remove it, a warning
208 * is in order. The warning is silly,
209 * though, if we're just replacing one
210 * symlink with another symlink.
211 */
212 + tail[0] = c;
213 + /* FIXME: not sure how important this is to restore
214 if (!S_ISLNK(a->mode)) {
215 - archive_set_error(&a->archive, 0,
216 - "Removing symlink %s",
217 - a->name);
218 + if(error_number) *error_number = 0;
219 + if(error_string)
220 + archive_string_sprintf(error_string,
221 + "Removing symlink %s",
222 + path);
223 }
224 + */
225 /* Symlink gone. No more problem! */
226 - pn[0] = c;
227 - return (0);
228 - } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
229 + res = (ARCHIVE_OK);
230 + break;
231 + } else if (flags & ARCHIVE_EXTRACT_UNLINK) {
232 /* User asked us to remove problems. */
233 - if (unlink(a->name) != 0) {
234 - archive_set_error(&a->archive, 0,
235 - "Cannot remove intervening symlink %s",
236 - a->name);
237 - pn[0] = c;
238 - return (ARCHIVE_FAILED);
239 + if (unlink(head) != 0) {
240 + tail[0] = c;
241 + if(error_number) *error_number = 0;
242 + if(error_string)
243 + archive_string_sprintf(error_string,
244 + "Cannot remove intervening symlink %s",
245 + path);
246 + res = (ARCHIVE_FAILED);
247 + break;
248 }
249 - a->pst = NULL;
250 } else {
251 - archive_set_error(&a->archive, 0,
252 - "Cannot extract through symlink %s",
253 - a->name);
254 - pn[0] = c;
255 - return (ARCHIVE_FAILED);
256 + tail[0] = c;
257 + if(error_number) *error_number = 0;
258 + if(error_string)
259 + archive_string_sprintf(error_string,
260 + "Cannot extract through symlink %s",
261 + path);
262 + res = (ARCHIVE_FAILED);
263 + break;
264 }
265 }
266 + /* be sure to always maintain this */
267 + tail[0] = c;
268 + }
269 + /* Catches loop exits via break */
270 + tail[0] = c;
271 +#ifdef HAVE_FCHDIR
272 + /* If we changed directory above, restore it here. */
273 + if (restore_pwd >= 0) {
274 + r = fchdir(restore_pwd);
275 + if (r != 0) {
276 + if(error_number) *error_number = 0;
277 + if(error_string)
278 + archive_string_sprintf(error_string,
279 + "Cannot extract through symlink %s",
280 + path);
281 + }
282 + close(restore_pwd);
283 + restore_pwd = -1;
284 + if (r != 0) {
285 + res = (ARCHIVE_FATAL);
286 + }
287 }
288 - pn[0] = c;
289 - /* We've checked and/or cleaned the whole path, so remember it. */
290 - archive_strcpy(&a->path_safe, a->name);
291 - return (ARCHIVE_OK);
292 +#endif
293 + /* TODO: reintroduce a safe cache here? */
294 + return res;
295 #endif
296 }
297
298 +/*
299 + * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
300 + * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
301 + */
302 +static int
303 +check_symlinks(struct archive_write_disk *a)
304 +{
305 + struct archive_string error_string;
306 + int error_number;
307 + int rc;
308 + archive_string_init(&error_string);
309 + rc = check_path_for_symlinks(a->name, &error_number, &error_string, a->flags);
310 + if (rc != ARCHIVE_OK) {
311 + archive_set_error(&a->archive, error_number, "%s", error_string.s);
312 + }
313 + archive_string_free(&error_string);
314 + a->pst = NULL; /* to be safe */
315 + return rc;
316 +}
317 +
318 +
319 #if defined(_WIN32) || defined(__CYGWIN__)
320 /*
321 * 1. Convert a path separator from '\' to '/' .

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed