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

Annotation 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 - (hide 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 jpp 1.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