1 /*-
2  * Copyright (c) 2009,2010 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: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
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 /* Generic initialization of 'struct archive' objects. */
54 int
55 __archive_clean(struct archive *a)
56 {
57 	archive_string_conversion_free(a);
58 	return (ARCHIVE_OK);
59 }
60 
61 int
62 archive_version_number(void)
63 {
64 	return (ARCHIVE_VERSION_NUMBER);
65 }
66 
67 const char *
68 archive_version_string(void)
69 {
70 	return (ARCHIVE_VERSION_STRING);
71 }
72 
73 int
74 archive_errno(struct archive *a)
75 {
76 	return (a->archive_error_number);
77 }
78 
79 const char *
80 archive_error_string(struct archive *a)
81 {
82 
83 	if (a->error != NULL  &&  *a->error != '\0')
84 		return (a->error);
85 	else
86 		return (NULL);
87 }
88 
89 int
90 archive_file_count(struct archive *a)
91 {
92 	return (a->file_count);
93 }
94 
95 int
96 archive_format(struct archive *a)
97 {
98 	return (a->archive_format);
99 }
100 
101 const char *
102 archive_format_name(struct archive *a)
103 {
104 	return (a->archive_format_name);
105 }
106 
107 
108 int
109 archive_compression(struct archive *a)
110 {
111 	return archive_filter_code(a, 0);
112 }
113 
114 const char *
115 archive_compression_name(struct archive *a)
116 {
117 	return archive_filter_name(a, 0);
118 }
119 
120 
121 /*
122  * Return a count of the number of compressed bytes processed.
123  */
124 int64_t
125 archive_position_compressed(struct archive *a)
126 {
127 	return archive_filter_bytes(a, -1);
128 }
129 
130 /*
131  * Return a count of the number of uncompressed bytes processed.
132  */
133 int64_t
134 archive_position_uncompressed(struct archive *a)
135 {
136 	return archive_filter_bytes(a, 0);
137 }
138 
139 void
140 archive_clear_error(struct archive *a)
141 {
142 	archive_string_empty(&a->error_string);
143 	a->error = NULL;
144 	a->archive_error_number = 0;
145 }
146 
147 void
148 archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
149 {
150 	va_list ap;
151 
152 	a->archive_error_number = error_number;
153 	if (fmt == NULL) {
154 		a->error = NULL;
155 		return;
156 	}
157 
158 	archive_string_empty(&(a->error_string));
159 	va_start(ap, fmt);
160 	archive_string_vsprintf(&(a->error_string), fmt, ap);
161 	va_end(ap);
162 	a->error = a->error_string.s;
163 }
164 
165 void
166 archive_copy_error(struct archive *dest, struct archive *src)
167 {
168 	dest->archive_error_number = src->archive_error_number;
169 
170 	archive_string_copy(&dest->error_string, &src->error_string);
171 	dest->error = dest->error_string.s;
172 }
173 
174 void
175 __archive_errx(int retvalue, const char *msg)
176 {
177 	static const char *msg1 = "Fatal Internal Error in libarchive: ";
178 	size_t s;
179 
180 	s = write(2, msg1, strlen(msg1));
181 	(void)s; /* UNUSED */
182 	s = write(2, msg, strlen(msg));
183 	(void)s; /* UNUSED */
184 	s = write(2, "\n", 1);
185 	(void)s; /* UNUSED */
186 	exit(retvalue);
187 }
188 
189 /*
190  * Create a temporary file
191  */
192 #if defined(_WIN32) && !defined(__CYGWIN__)
193 
194 /*
195  * Do not use Windows tmpfile() function.
196  * It will make a temporary file under the root directory
197  * and it'll cause permission error if a user who is
198  * non-Administrator creates temporary files.
199  * Also Windows version of mktemp family including _mktemp_s
200  * are not secure.
201  */
202 int
203 __archive_mktemp(const char *tmpdir)
204 {
205 	static const wchar_t num[] = {
206 		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
207 		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
208 		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
209 		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
210 		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
211 		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
212 		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
213 		L'u', L'v', L'w', L'x', L'y', L'z'
214 	};
215 	HCRYPTPROV hProv;
216 	struct archive_wstring temp_name;
217 	wchar_t *ws;
218 	DWORD attr;
219 	wchar_t *xp, *ep;
220 	int fd;
221 
222 	hProv = (HCRYPTPROV)NULL;
223 	fd = -1;
224 	ws = NULL;
225 	archive_string_init(&temp_name);
226 
227 	/* Get a temporary directory. */
228 	if (tmpdir == NULL) {
229 		size_t l;
230 		wchar_t *tmp;
231 
232 		l = GetTempPathW(0, NULL);
233 		if (l == 0) {
234 			la_dosmaperr(GetLastError());
235 			goto exit_tmpfile;
236 		}
237 		tmp = malloc(l*sizeof(wchar_t));
238 		if (tmp == NULL) {
239 			errno = ENOMEM;
240 			goto exit_tmpfile;
241 		}
242 		GetTempPathW(l, tmp);
243 		archive_wstrcpy(&temp_name, tmp);
244 		free(tmp);
245 	} else {
246 		if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
247 		    strlen(tmpdir)) < 0)
248 			goto exit_tmpfile;
249 		if (temp_name.s[temp_name.length-1] != L'/')
250 			archive_wstrappend_wchar(&temp_name, L'/');
251 	}
252 
253 	/* Check if temp_name is a directory. */
254 	attr = GetFileAttributesW(temp_name.s);
255 	if (attr == (DWORD)-1) {
256 		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
257 			la_dosmaperr(GetLastError());
258 			goto exit_tmpfile;
259 		}
260 		ws = __la_win_permissive_name_w(temp_name.s);
261 		if (ws == NULL) {
262 			errno = EINVAL;
263 			goto exit_tmpfile;
264 		}
265 		attr = GetFileAttributesW(ws);
266 		if (attr == (DWORD)-1) {
267 			la_dosmaperr(GetLastError());
268 			goto exit_tmpfile;
269 		}
270 	}
271 	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
272 		errno = ENOTDIR;
273 		goto exit_tmpfile;
274 	}
275 
276 	/*
277 	 * Create a temporary file.
278 	 */
279 	archive_wstrcat(&temp_name, L"libarchive_");
280 	xp = temp_name.s + archive_strlen(&temp_name);
281 	archive_wstrcat(&temp_name, L"XXXXXXXXXX");
282 	ep = temp_name.s + archive_strlen(&temp_name);
283 
284 	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
285 		CRYPT_VERIFYCONTEXT)) {
286 		la_dosmaperr(GetLastError());
287 		goto exit_tmpfile;
288 	}
289 
290 	for (;;) {
291 		wchar_t *p;
292 		HANDLE h;
293 
294 		/* Generate a random file name through CryptGenRandom(). */
295 		p = xp;
296 		if (!CryptGenRandom(hProv, (ep - p)*sizeof(wchar_t), (BYTE*)p)) {
297 			la_dosmaperr(GetLastError());
298 			goto exit_tmpfile;
299 		}
300 		for (; p < ep; p++)
301 			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
302 
303 		free(ws);
304 		ws = __la_win_permissive_name_w(temp_name.s);
305 		if (ws == NULL) {
306 			errno = EINVAL;
307 			goto exit_tmpfile;
308 		}
309 		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
310 		 * delete this temporary file immediately when this
311 		 * file closed. */
312 		h = CreateFileW(ws,
313 		    GENERIC_READ | GENERIC_WRITE | DELETE,
314 		    0,/* Not share */
315 		    NULL,
316 		    CREATE_NEW,/* Create a new file only */
317 		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
318 		    NULL);
319 		if (h == INVALID_HANDLE_VALUE) {
320 			/* The same file already exists. retry with
321 			 * a new filename. */
322 			if (GetLastError() == ERROR_FILE_EXISTS)
323 				continue;
324 			/* Otherwise, fail creation temporary file. */
325 			la_dosmaperr(GetLastError());
326 			goto exit_tmpfile;
327 		}
328 		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
329 		if (fd == -1) {
330 			CloseHandle(h);
331 			goto exit_tmpfile;
332 		} else
333 			break;/* success! */
334 	}
335 exit_tmpfile:
336 	if (hProv != (HCRYPTPROV)NULL)
337 		CryptReleaseContext(hProv, 0);
338 	free(ws);
339 	archive_wstring_free(&temp_name);
340 	return (fd);
341 }
342 
343 #else
344 
345 static int
346 get_tempdir(struct archive_string *temppath)
347 {
348 	const char *tmp;
349 
350 	tmp = getenv("TMPDIR");
351 	if (tmp == NULL)
352 #ifdef _PATH_TMP
353 		tmp = _PATH_TMP;
354 #else
355                 tmp = "/tmp";
356 #endif
357 	archive_strcpy(temppath, tmp);
358 	if (temppath->s[temppath->length-1] != '/')
359 		archive_strappend_char(temppath, '/');
360 	return (ARCHIVE_OK);
361 }
362 
363 #if defined(HAVE_MKSTEMP)
364 
365 /*
366  * We can use mkstemp().
367  */
368 
369 int
370 __archive_mktemp(const char *tmpdir)
371 {
372 	struct archive_string temp_name;
373 	int fd = -1;
374 
375 	archive_string_init(&temp_name);
376 	if (tmpdir == NULL) {
377 		if (get_tempdir(&temp_name) != ARCHIVE_OK)
378 			goto exit_tmpfile;
379 	} else {
380 		archive_strcpy(&temp_name, tmpdir);
381 		if (temp_name.s[temp_name.length-1] != '/')
382 			archive_strappend_char(&temp_name, '/');
383 	}
384 	archive_strcat(&temp_name, "libarchive_XXXXXX");
385 	fd = mkstemp(temp_name.s);
386 	if (fd < 0)
387 		goto exit_tmpfile;
388 	unlink(temp_name.s);
389 exit_tmpfile:
390 	archive_string_free(&temp_name);
391 	return (fd);
392 }
393 
394 #else
395 
396 /*
397  * We use a private routine.
398  */
399 
400 int
401 __archive_mktemp(const char *tmpdir)
402 {
403         static const char num[] = {
404 		'0', '1', '2', '3', '4', '5', '6', '7',
405 		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
406 		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
407 		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
408 		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
409 		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
410 		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
411 		'u', 'v', 'w', 'x', 'y', 'z'
412         };
413 	struct archive_string temp_name;
414 	struct stat st;
415 	int fd;
416 	char *tp, *ep;
417 	unsigned seed;
418 
419 	fd = -1;
420 	archive_string_init(&temp_name);
421 	if (tmpdir == NULL) {
422 		if (get_tempdir(&temp_name) != ARCHIVE_OK)
423 			goto exit_tmpfile;
424 	} else
425 		archive_strcpy(&temp_name, tmpdir);
426 	if (temp_name.s[temp_name.length-1] == '/') {
427 		temp_name.s[temp_name.length-1] = '\0';
428 		temp_name.length --;
429 	}
430 	if (stat(temp_name.s, &st) < 0)
431 		goto exit_tmpfile;
432 	if (!S_ISDIR(st.st_mode)) {
433 		errno = ENOTDIR;
434 		goto exit_tmpfile;
435 	}
436 	archive_strcat(&temp_name, "/libarchive_");
437 	tp = temp_name.s + archive_strlen(&temp_name);
438 	archive_strcat(&temp_name, "XXXXXXXXXX");
439 	ep = temp_name.s + archive_strlen(&temp_name);
440 
441 	fd = open("/dev/random", O_RDONLY);
442 	if (fd < 0)
443 		seed = time(NULL);
444 	else {
445 		if (read(fd, &seed, sizeof(seed)) < 0)
446 			seed = time(NULL);
447 		close(fd);
448 	}
449 	do {
450 		char *p;
451 
452 		p = tp;
453 		while (p < ep)
454 			*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
455 		fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600);
456 	} while (fd < 0 && errno == EEXIST);
457 	if (fd < 0)
458 		goto exit_tmpfile;
459 	unlink(temp_name.s);
460 exit_tmpfile:
461 	archive_string_free(&temp_name);
462 	return (fd);
463 }
464 
465 #endif /* HAVE_MKSTEMP */
466 #endif /* !_WIN32 || __CYGWIN__ */
467