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 		archive_wstring_append_from_mbs(&temp_name, tmpdir,
247 		    strlen(tmpdir));
248 		if (temp_name.s[temp_name.length-1] != L'/')
249 			archive_wstrappend_wchar(&temp_name, L'/');
250 	}
251 
252 	/* Check if temp_name is a directory. */
253 	attr = GetFileAttributesW(temp_name.s);
254 	if (attr == (DWORD)-1) {
255 		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
256 			la_dosmaperr(GetLastError());
257 			goto exit_tmpfile;
258 		}
259 		ws = __la_win_permissive_name_w(temp_name.s);
260 		if (ws == NULL) {
261 			errno = EINVAL;
262 			goto exit_tmpfile;
263 		}
264 		attr = GetFileAttributesW(ws);
265 		if (attr == (DWORD)-1) {
266 			la_dosmaperr(GetLastError());
267 			goto exit_tmpfile;
268 		}
269 	}
270 	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
271 		errno = ENOTDIR;
272 		goto exit_tmpfile;
273 	}
274 
275 	/*
276 	 * Create a temporary file.
277 	 */
278 	archive_wstrcat(&temp_name, L"libarchive_");
279 	xp = temp_name.s + archive_strlen(&temp_name);
280 	archive_wstrcat(&temp_name, L"XXXXXXXXXX");
281 	ep = temp_name.s + archive_strlen(&temp_name);
282 
283 	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
284 		CRYPT_VERIFYCONTEXT)) {
285 		la_dosmaperr(GetLastError());
286 		goto exit_tmpfile;
287 	}
288 
289 	for (;;) {
290 		wchar_t *p;
291 		HANDLE h;
292 
293 		/* Generate a random file name through CryptGenRandom(). */
294 		p = xp;
295 		if (!CryptGenRandom(hProv, (ep - p)*sizeof(wchar_t), (BYTE*)p)) {
296 			la_dosmaperr(GetLastError());
297 			goto exit_tmpfile;
298 		}
299 		for (; p < ep; p++)
300 			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
301 
302 		free(ws);
303 		ws = __la_win_permissive_name_w(temp_name.s);
304 		if (ws == NULL) {
305 			errno = EINVAL;
306 			goto exit_tmpfile;
307 		}
308 		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
309 		 * delete this temporary file immediately when this
310 		 * file closed. */
311 		h = CreateFileW(ws,
312 		    GENERIC_READ | GENERIC_WRITE | DELETE,
313 		    0,/* Not share */
314 		    NULL,
315 		    CREATE_NEW,/* Create a new file only */
316 		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
317 		    NULL);
318 		if (h == INVALID_HANDLE_VALUE) {
319 			/* The same file already exists. retry with
320 			 * a new filename. */
321 			if (GetLastError() == ERROR_FILE_EXISTS)
322 				continue;
323 			/* Otherwise, fail creation temporary file. */
324 			la_dosmaperr(GetLastError());
325 			goto exit_tmpfile;
326 		}
327 		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
328 		if (fd == -1) {
329 			CloseHandle(h);
330 			goto exit_tmpfile;
331 		} else
332 			break;/* success! */
333 	}
334 exit_tmpfile:
335 	if (hProv != (HCRYPTPROV)NULL)
336 		CryptReleaseContext(hProv, 0);
337 	free(ws);
338 	archive_wstring_free(&temp_name);
339 	return (fd);
340 }
341 
342 #else
343 
344 static int
345 get_tempdir(struct archive_string *temppath)
346 {
347 	const char *tmp;
348 
349 	tmp = getenv("TMPDIR");
350 	if (tmp == NULL)
351 #ifdef _PATH_TMP
352 		tmp = _PATH_TMP;
353 #else
354                 tmp = "/tmp";
355 #endif
356 	archive_strcpy(temppath, tmp);
357 	if (temppath->s[temppath->length-1] != '/')
358 		archive_strappend_char(temppath, '/');
359 	return (ARCHIVE_OK);
360 }
361 
362 #if defined(HAVE_MKSTEMP)
363 
364 /*
365  * We can use mkstemp().
366  */
367 
368 int
369 __archive_mktemp(const char *tmpdir)
370 {
371 	struct archive_string temp_name;
372 	int fd = -1;
373 
374 	archive_string_init(&temp_name);
375 	if (tmpdir == NULL) {
376 		if (get_tempdir(&temp_name) != ARCHIVE_OK)
377 			goto exit_tmpfile;
378 	} else {
379 		archive_strcpy(&temp_name, tmpdir);
380 		if (temp_name.s[temp_name.length-1] != '/')
381 			archive_strappend_char(&temp_name, '/');
382 	}
383 	archive_strcat(&temp_name, "libarchive_XXXXXX");
384 	fd = mkstemp(temp_name.s);
385 	if (fd < 0)
386 		goto exit_tmpfile;
387 	unlink(temp_name.s);
388 exit_tmpfile:
389 	archive_string_free(&temp_name);
390 	return (fd);
391 }
392 
393 #else
394 
395 /*
396  * We use a private routine.
397  */
398 
399 int
400 __archive_mktemp(const char *tmpdir)
401 {
402         static const char num[] = {
403 		'0', '1', '2', '3', '4', '5', '6', '7',
404 		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
405 		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
406 		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
407 		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
408 		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
409 		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
410 		'u', 'v', 'w', 'x', 'y', 'z'
411         };
412 	struct archive_string temp_name;
413 	struct stat st;
414 	int fd;
415 	char *tp, *ep;
416 	unsigned seed;
417 
418 	fd = -1;
419 	archive_string_init(&temp_name);
420 	if (tmpdir == NULL) {
421 		if (get_tempdir(&temp_name) != ARCHIVE_OK)
422 			goto exit_tmpfile;
423 	} else
424 		archive_strcpy(&temp_name, tmpdir);
425 	if (temp_name.s[temp_name.length-1] == '/') {
426 		temp_name.s[temp_name.length-1] = '\0';
427 		temp_name.length --;
428 	}
429 	if (stat(temp_name.s, &st) < 0)
430 		goto exit_tmpfile;
431 	if (!S_ISDIR(st.st_mode)) {
432 		errno = ENOTDIR;
433 		goto exit_tmpfile;
434 	}
435 	archive_strcat(&temp_name, "/libarchive_");
436 	tp = temp_name.s + archive_strlen(&temp_name);
437 	archive_strcat(&temp_name, "XXXXXXXXXX");
438 	ep = temp_name.s + archive_strlen(&temp_name);
439 
440 	fd = open("/dev/random", O_RDONLY);
441 	if (fd < 0)
442 		seed = time(NULL);
443 	else {
444 		if (read(fd, &seed, sizeof(seed)) < 0)
445 			seed = time(NULL);
446 		close(fd);
447 	}
448 	do {
449 		char *p;
450 
451 		p = tp;
452 		while (p < ep)
453 			*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
454 		fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600);
455 	} while (fd < 0 && errno == EEXIST);
456 	if (fd < 0)
457 		goto exit_tmpfile;
458 	unlink(temp_name.s);
459 exit_tmpfile:
460 	archive_string_free(&temp_name);
461 	return (fd);
462 }
463 
464 #endif /* HAVE_MKSTEMP */
465 #endif /* !_WIN32 || __CYGWIN__ */
466