]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_util.c
Backport security fix for absolute path traversal vulnerability in bsdcpio.
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_util.c
1 /*-
2  * Copyright (c) 2009-2012 Michihiro NAKAJIMA
3  * Copyright (c) 2003-2007 Tim Kientzle
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD$");
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36 #ifdef HAVE_FCNTL_H
37 #include <fcntl.h>
38 #endif
39 #ifdef HAVE_STDLIB_H
40 #include <stdlib.h>
41 #endif
42 #ifdef HAVE_STRING_H
43 #include <string.h>
44 #endif
45 #if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
46 #include <wincrypt.h>
47 #endif
48
49 #include "archive.h"
50 #include "archive_private.h"
51 #include "archive_string.h"
52
53 #ifndef O_CLOEXEC
54 #define O_CLOEXEC       0
55 #endif
56
57 /* Generic initialization of 'struct archive' objects. */
58 int
59 __archive_clean(struct archive *a)
60 {
61         archive_string_conversion_free(a);
62         return (ARCHIVE_OK);
63 }
64
65 int
66 archive_version_number(void)
67 {
68         return (ARCHIVE_VERSION_NUMBER);
69 }
70
71 const char *
72 archive_version_string(void)
73 {
74         return (ARCHIVE_VERSION_STRING);
75 }
76
77 int
78 archive_errno(struct archive *a)
79 {
80         return (a->archive_error_number);
81 }
82
83 const char *
84 archive_error_string(struct archive *a)
85 {
86
87         if (a->error != NULL  &&  *a->error != '\0')
88                 return (a->error);
89         else
90                 return (NULL);
91 }
92
93 int
94 archive_file_count(struct archive *a)
95 {
96         return (a->file_count);
97 }
98
99 int
100 archive_format(struct archive *a)
101 {
102         return (a->archive_format);
103 }
104
105 const char *
106 archive_format_name(struct archive *a)
107 {
108         return (a->archive_format_name);
109 }
110
111
112 int
113 archive_compression(struct archive *a)
114 {
115         return archive_filter_code(a, 0);
116 }
117
118 const char *
119 archive_compression_name(struct archive *a)
120 {
121         return archive_filter_name(a, 0);
122 }
123
124
125 /*
126  * Return a count of the number of compressed bytes processed.
127  */
128 int64_t
129 archive_position_compressed(struct archive *a)
130 {
131         return archive_filter_bytes(a, -1);
132 }
133
134 /*
135  * Return a count of the number of uncompressed bytes processed.
136  */
137 int64_t
138 archive_position_uncompressed(struct archive *a)
139 {
140         return archive_filter_bytes(a, 0);
141 }
142
143 void
144 archive_clear_error(struct archive *a)
145 {
146         archive_string_empty(&a->error_string);
147         a->error = NULL;
148         a->archive_error_number = 0;
149 }
150
151 void
152 archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
153 {
154         va_list ap;
155
156         a->archive_error_number = error_number;
157         if (fmt == NULL) {
158                 a->error = NULL;
159                 return;
160         }
161
162         archive_string_empty(&(a->error_string));
163         va_start(ap, fmt);
164         archive_string_vsprintf(&(a->error_string), fmt, ap);
165         va_end(ap);
166         a->error = a->error_string.s;
167 }
168
169 void
170 archive_copy_error(struct archive *dest, struct archive *src)
171 {
172         dest->archive_error_number = src->archive_error_number;
173
174         archive_string_copy(&dest->error_string, &src->error_string);
175         dest->error = dest->error_string.s;
176 }
177
178 void
179 __archive_errx(int retvalue, const char *msg)
180 {
181         static const char *msg1 = "Fatal Internal Error in libarchive: ";
182         size_t s;
183
184         s = write(2, msg1, strlen(msg1));
185         (void)s; /* UNUSED */
186         s = write(2, msg, strlen(msg));
187         (void)s; /* UNUSED */
188         s = write(2, "\n", 1);
189         (void)s; /* UNUSED */
190         exit(retvalue);
191 }
192
193 /*
194  * Create a temporary file
195  */
196 #if defined(_WIN32) && !defined(__CYGWIN__)
197
198 /*
199  * Do not use Windows tmpfile() function.
200  * It will make a temporary file under the root directory
201  * and it'll cause permission error if a user who is
202  * non-Administrator creates temporary files.
203  * Also Windows version of mktemp family including _mktemp_s
204  * are not secure.
205  */
206 int
207 __archive_mktemp(const char *tmpdir)
208 {
209         static const wchar_t num[] = {
210                 L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
211                 L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
212                 L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
213                 L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
214                 L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
215                 L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
216                 L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
217                 L'u', L'v', L'w', L'x', L'y', L'z'
218         };
219         HCRYPTPROV hProv;
220         struct archive_wstring temp_name;
221         wchar_t *ws;
222         DWORD attr;
223         wchar_t *xp, *ep;
224         int fd;
225
226         hProv = (HCRYPTPROV)NULL;
227         fd = -1;
228         ws = NULL;
229         archive_string_init(&temp_name);
230
231         /* Get a temporary directory. */
232         if (tmpdir == NULL) {
233                 size_t l;
234                 wchar_t *tmp;
235
236                 l = GetTempPathW(0, NULL);
237                 if (l == 0) {
238                         la_dosmaperr(GetLastError());
239                         goto exit_tmpfile;
240                 }
241                 tmp = malloc(l*sizeof(wchar_t));
242                 if (tmp == NULL) {
243                         errno = ENOMEM;
244                         goto exit_tmpfile;
245                 }
246                 GetTempPathW((DWORD)l, tmp);
247                 archive_wstrcpy(&temp_name, tmp);
248                 free(tmp);
249         } else {
250                 if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
251                     strlen(tmpdir)) < 0)
252                         goto exit_tmpfile;
253                 if (temp_name.s[temp_name.length-1] != L'/')
254                         archive_wstrappend_wchar(&temp_name, L'/');
255         }
256
257         /* Check if temp_name is a directory. */
258         attr = GetFileAttributesW(temp_name.s);
259         if (attr == (DWORD)-1) {
260                 if (GetLastError() != ERROR_FILE_NOT_FOUND) {
261                         la_dosmaperr(GetLastError());
262                         goto exit_tmpfile;
263                 }
264                 ws = __la_win_permissive_name_w(temp_name.s);
265                 if (ws == NULL) {
266                         errno = EINVAL;
267                         goto exit_tmpfile;
268                 }
269                 attr = GetFileAttributesW(ws);
270                 if (attr == (DWORD)-1) {
271                         la_dosmaperr(GetLastError());
272                         goto exit_tmpfile;
273                 }
274         }
275         if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
276                 errno = ENOTDIR;
277                 goto exit_tmpfile;
278         }
279
280         /*
281          * Create a temporary file.
282          */
283         archive_wstrcat(&temp_name, L"libarchive_");
284         xp = temp_name.s + archive_strlen(&temp_name);
285         archive_wstrcat(&temp_name, L"XXXXXXXXXX");
286         ep = temp_name.s + archive_strlen(&temp_name);
287
288         if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
289                 CRYPT_VERIFYCONTEXT)) {
290                 la_dosmaperr(GetLastError());
291                 goto exit_tmpfile;
292         }
293
294         for (;;) {
295                 wchar_t *p;
296                 HANDLE h;
297
298                 /* Generate a random file name through CryptGenRandom(). */
299                 p = xp;
300                 if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
301                     (BYTE*)p)) {
302                         la_dosmaperr(GetLastError());
303                         goto exit_tmpfile;
304                 }
305                 for (; p < ep; p++)
306                         *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
307
308                 free(ws);
309                 ws = __la_win_permissive_name_w(temp_name.s);
310                 if (ws == NULL) {
311                         errno = EINVAL;
312                         goto exit_tmpfile;
313                 }
314                 /* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
315                  * delete this temporary file immediately when this
316                  * file closed. */
317                 h = CreateFileW(ws,
318                     GENERIC_READ | GENERIC_WRITE | DELETE,
319                     0,/* Not share */
320                     NULL,
321                     CREATE_NEW,/* Create a new file only */
322                     FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
323                     NULL);
324                 if (h == INVALID_HANDLE_VALUE) {
325                         /* The same file already exists. retry with
326                          * a new filename. */
327                         if (GetLastError() == ERROR_FILE_EXISTS)
328                                 continue;
329                         /* Otherwise, fail creation temporary file. */
330                         la_dosmaperr(GetLastError());
331                         goto exit_tmpfile;
332                 }
333                 fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
334                 if (fd == -1) {
335                         CloseHandle(h);
336                         goto exit_tmpfile;
337                 } else
338                         break;/* success! */
339         }
340 exit_tmpfile:
341         if (hProv != (HCRYPTPROV)NULL)
342                 CryptReleaseContext(hProv, 0);
343         free(ws);
344         archive_wstring_free(&temp_name);
345         return (fd);
346 }
347
348 #else
349
350 static int
351 get_tempdir(struct archive_string *temppath)
352 {
353         const char *tmp;
354
355         tmp = getenv("TMPDIR");
356         if (tmp == NULL)
357 #ifdef _PATH_TMP
358                 tmp = _PATH_TMP;
359 #else
360                 tmp = "/tmp";
361 #endif
362         archive_strcpy(temppath, tmp);
363         if (temppath->s[temppath->length-1] != '/')
364                 archive_strappend_char(temppath, '/');
365         return (ARCHIVE_OK);
366 }
367
368 #if defined(HAVE_MKSTEMP)
369
370 /*
371  * We can use mkstemp().
372  */
373
374 int
375 __archive_mktemp(const char *tmpdir)
376 {
377         struct archive_string temp_name;
378         int fd = -1;
379
380         archive_string_init(&temp_name);
381         if (tmpdir == NULL) {
382                 if (get_tempdir(&temp_name) != ARCHIVE_OK)
383                         goto exit_tmpfile;
384         } else {
385                 archive_strcpy(&temp_name, tmpdir);
386                 if (temp_name.s[temp_name.length-1] != '/')
387                         archive_strappend_char(&temp_name, '/');
388         }
389         archive_strcat(&temp_name, "libarchive_XXXXXX");
390         fd = mkstemp(temp_name.s);
391         if (fd < 0)
392                 goto exit_tmpfile;
393         __archive_ensure_cloexec_flag(fd);
394         unlink(temp_name.s);
395 exit_tmpfile:
396         archive_string_free(&temp_name);
397         return (fd);
398 }
399
400 #else
401
402 /*
403  * We use a private routine.
404  */
405
406 int
407 __archive_mktemp(const char *tmpdir)
408 {
409         static const char num[] = {
410                 '0', '1', '2', '3', '4', '5', '6', '7',
411                 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
412                 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
413                 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
414                 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
415                 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
416                 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
417                 'u', 'v', 'w', 'x', 'y', 'z'
418         };
419         struct archive_string temp_name;
420         struct stat st;
421         int fd;
422         char *tp, *ep;
423         unsigned seed;
424
425         fd = -1;
426         archive_string_init(&temp_name);
427         if (tmpdir == NULL) {
428                 if (get_tempdir(&temp_name) != ARCHIVE_OK)
429                         goto exit_tmpfile;
430         } else
431                 archive_strcpy(&temp_name, tmpdir);
432         if (temp_name.s[temp_name.length-1] == '/') {
433                 temp_name.s[temp_name.length-1] = '\0';
434                 temp_name.length --;
435         }
436         if (stat(temp_name.s, &st) < 0)
437                 goto exit_tmpfile;
438         if (!S_ISDIR(st.st_mode)) {
439                 errno = ENOTDIR;
440                 goto exit_tmpfile;
441         }
442         archive_strcat(&temp_name, "/libarchive_");
443         tp = temp_name.s + archive_strlen(&temp_name);
444         archive_strcat(&temp_name, "XXXXXXXXXX");
445         ep = temp_name.s + archive_strlen(&temp_name);
446
447         fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
448         __archive_ensure_cloexec_flag(fd);
449         if (fd < 0)
450                 seed = time(NULL);
451         else {
452                 if (read(fd, &seed, sizeof(seed)) < 0)
453                         seed = time(NULL);
454                 close(fd);
455         }
456         do {
457                 char *p;
458
459                 p = tp;
460                 while (p < ep)
461                         *p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
462                 fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
463                           0600);
464         } while (fd < 0 && errno == EEXIST);
465         if (fd < 0)
466                 goto exit_tmpfile;
467         __archive_ensure_cloexec_flag(fd);
468         unlink(temp_name.s);
469 exit_tmpfile:
470         archive_string_free(&temp_name);
471         return (fd);
472 }
473
474 #endif /* HAVE_MKSTEMP */
475 #endif /* !_WIN32 || __CYGWIN__ */
476
477 /*
478  * Set FD_CLOEXEC flag to a file descriptor if it is not set.
479  * We have to set the flag if the platform does not provide O_CLOEXEC
480  * or F_DUPFD_CLOEXEC flags.
481  *
482  * Note: This function is absolutely called after creating a new file
483  * descriptor even if the platform seemingly provides O_CLOEXEC or
484  * F_DUPFD_CLOEXEC macros because it is possible that the platform
485  * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
486  */
487 void
488 __archive_ensure_cloexec_flag(int fd)
489 {
490 #if defined(_WIN32) && !defined(__CYGWIN__)
491         (void)fd; /* UNSED */
492 #else
493         int flags;
494
495         if (fd >= 0) {
496                 flags = fcntl(fd, F_GETFD);
497                 if (flags != -1 && (flags & FD_CLOEXEC) == 0)
498                         fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
499         }
500 #endif
501 }