1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "test.h"
26 
27 #if !defined(_WIN32) || defined(__CYGWIN__)
28 
29 #define UMASK 022
30 
31 static long _default_gid = -1;
32 static long _invalid_gid = -1;
33 static long _alt_gid = -1;
34 
35 /*
36  * To fully test SGID restores, we need three distinct GIDs to work
37  * with:
38  *    * the GID that files are created with by default (for the
39  *      current user in the current directory)
40  *    * An "alt gid" that this user can create files with
41  *    * An "invalid gid" that this user is not permitted to create
42  *      files with.
43  * The second fails if this user doesn't belong to at least two groups;
44  * the third fails if the current user is root.
45  */
46 static void
47 searchgid(void)
48 {
49 	static int   _searched = 0;
50 	uid_t uid = getuid();
51 	gid_t gid = 0;
52 	unsigned int n;
53 	struct stat st;
54 	int fd;
55 
56 	/* If we've already looked this up, we're done. */
57 	if (_searched)
58 		return;
59 	_searched = 1;
60 
61 	/* Create a file on disk in the current default dir. */
62 	fd = open("test_gid", O_CREAT | O_BINARY, 0664);
63 	failure("Couldn't create a file for gid testing.");
64 	assert(fd > 0);
65 
66 	/* See what GID it ended up with.  This is our "valid" GID. */
67 	assert(fstat(fd, &st) == 0);
68 	_default_gid = st.st_gid;
69 
70 	/* Find a GID for which fchown() fails.  This is our "invalid" GID. */
71 	_invalid_gid = -1;
72 	/* This loop stops when we wrap the gid or examine 10,000 gids. */
73 	for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) {
74 		if (fchown(fd, uid, gid) != 0) {
75 			_invalid_gid = gid;
76 			break;
77 		}
78 	}
79 
80 	/*
81 	 * Find a GID for which fchown() succeeds, but which isn't the
82 	 * default.  This is the "alternate" gid.
83 	 */
84 	_alt_gid = -1;
85 	for (gid = 0, n = 0; gid == n && n < 10000 ; n++, gid++) {
86 		/* _alt_gid must be different than _default_gid */
87 		if (gid == (gid_t)_default_gid)
88 			continue;
89 		if (fchown(fd, uid, gid) == 0) {
90 			_alt_gid = gid;
91 			break;
92 		}
93 	}
94 	close(fd);
95 }
96 
97 static int
98 altgid(void)
99 {
100 	searchgid();
101 	return (_alt_gid);
102 }
103 
104 static int
105 invalidgid(void)
106 {
107 	searchgid();
108 	return (_invalid_gid);
109 }
110 
111 static int
112 defaultgid(void)
113 {
114 	searchgid();
115 	return (_default_gid);
116 }
117 #endif
118 
119 /*
120  * Exercise permission and ownership restores.
121  * In particular, try to exercise a bunch of border cases related
122  * to files/dirs that already exist, SUID/SGID bits, etc.
123  */
124 
125 DEFINE_TEST(test_write_disk_perms)
126 {
127 #if defined(_WIN32) && !defined(__CYGWIN__)
128 	skipping("archive_write_disk interface");
129 #else
130 	struct archive *a;
131 	struct archive_entry *ae;
132 	struct stat st;
133 	uid_t original_uid;
134 	uid_t try_to_change_uid;
135 
136 	assertUmask(UMASK);
137 
138 	/*
139 	 * Set ownership of the current directory to the group of this
140 	 * process.  Otherwise, the SGID tests below fail if the
141 	 * /tmp directory is owned by a group to which we don't belong
142 	 * and we're on a system where group ownership is inherited.
143 	 * (Because we're not allowed to SGID files with defaultgid().)
144 	 */
145 	assertEqualInt(0, chown(".", getuid(), getgid()));
146 
147 	/* Create an archive_write_disk object. */
148 	assert((a = archive_write_disk_new()) != NULL);
149 
150 	/* Write a regular file to it. */
151 	assert((ae = archive_entry_new()) != NULL);
152 	archive_entry_copy_pathname(ae, "file_0755");
153 	archive_entry_set_mode(ae, S_IFREG | 0777);
154 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
155 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
156 	archive_entry_free(ae);
157 
158 	/* Write a regular file, then write over it. */
159 	/* For files, the perms should get updated. */
160 	assert((ae = archive_entry_new()) != NULL);
161 	archive_entry_copy_pathname(ae, "file_overwrite_0144");
162 	archive_entry_set_mode(ae, S_IFREG | 0777);
163 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
164 	archive_entry_free(ae);
165 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
166 	/* Check that file was created with different perms. */
167 	assertEqualInt(0, stat("file_overwrite_0144", &st));
168 	failure("file_overwrite_0144: st.st_mode=%o", st.st_mode);
169 	assert((st.st_mode & 07777) != 0144);
170 	/* Overwrite, this should change the perms. */
171 	assert((ae = archive_entry_new()) != NULL);
172 	archive_entry_copy_pathname(ae, "file_overwrite_0144");
173 	archive_entry_set_mode(ae, S_IFREG | 0144);
174 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
175 	archive_entry_free(ae);
176 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
177 
178 	/* Write a regular dir. */
179 	assert((ae = archive_entry_new()) != NULL);
180 	archive_entry_copy_pathname(ae, "dir_0514");
181 	archive_entry_set_mode(ae, S_IFDIR | 0514);
182 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
183 	archive_entry_free(ae);
184 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
185 
186 	/* Overwrite an existing dir. */
187 	/* For dir, the first perms should get left. */
188 	assertMakeDir("dir_overwrite_0744", 0744);
189 	/* Check original perms. */
190 	assertEqualInt(0, stat("dir_overwrite_0744", &st));
191 	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
192 	assertEqualInt(st.st_mode & 0777, 0744);
193 	/* Overwrite shouldn't edit perms. */
194 	assert((ae = archive_entry_new()) != NULL);
195 	archive_entry_copy_pathname(ae, "dir_overwrite_0744");
196 	archive_entry_set_mode(ae, S_IFDIR | 0777);
197 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
198 	archive_entry_free(ae);
199 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
200 	/* Make sure they're unchanged. */
201 	assertEqualInt(0, stat("dir_overwrite_0744", &st));
202 	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
203 	assertEqualInt(st.st_mode & 0777, 0744);
204 
205 	/* For dir, the owner should get left when not overwriting. */
206 	assertMakeDir("dir_owner", 0744);
207 
208 	if (getuid() == 0) {
209 		original_uid = getuid() + 1;
210 		try_to_change_uid = getuid();
211 		assertEqualInt(0, chown("dir_owner", original_uid, getgid()));
212 	} else {
213 		original_uid = getuid();
214 		try_to_change_uid = getuid() + 1;
215 	}
216 
217 	/* Check original owner. */
218 	assertEqualInt(0, stat("dir_owner", &st));
219 	failure("dir_owner: st.st_uid=%d", st.st_uid);
220 	assertEqualInt(st.st_uid, original_uid);
221 	/* Shouldn't try to edit the owner when no overwrite option is set. */
222 	assert((ae = archive_entry_new()) != NULL);
223 	archive_entry_copy_pathname(ae, "dir_owner");
224 	archive_entry_set_mode(ae, S_IFDIR | 0744);
225 	archive_entry_set_uid(ae, try_to_change_uid);
226 	archive_write_disk_set_options(a,
227 	    ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_NO_OVERWRITE);
228 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
229 	archive_entry_free(ae);
230 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
231 	/* Make sure they're unchanged. */
232 	assertEqualInt(0, stat("dir_owner", &st));
233 	failure("dir_owner: st.st_uid=%d", st.st_uid);
234 	assertEqualInt(st.st_uid, original_uid);
235 
236 	/* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */
237 	assert((ae = archive_entry_new()) != NULL);
238 	archive_entry_copy_pathname(ae, "file_no_suid");
239 	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0777);
240 	archive_write_disk_set_options(a, 0);
241 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
242 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
243 
244 	/* Write a regular file with ARCHIVE_EXTRACT_PERM. */
245 	assert(archive_entry_clear(ae) != NULL);
246 	archive_entry_copy_pathname(ae, "file_0777");
247 	archive_entry_set_mode(ae, S_IFREG | 0777);
248 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
249 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
250 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
251 
252 	/* Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit */
253 	assert(archive_entry_clear(ae) != NULL);
254 	archive_entry_copy_pathname(ae, "file_4742");
255 	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
256 	archive_entry_set_uid(ae, getuid());
257 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
258 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
259 	assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
260 
261 	/*
262 	 * Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit,
263 	 * but wrong uid.  POSIX says you shouldn't restore SUID bit
264 	 * unless the UID could be restored.
265 	 */
266 	assert(archive_entry_clear(ae) != NULL);
267 	archive_entry_copy_pathname(ae, "file_bad_suid");
268 	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
269 	archive_entry_set_uid(ae, getuid() + 1);
270 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
271 	assertA(0 == archive_write_header(a, ae));
272 	/*
273 	 * Because we didn't ask for owner, the failure to
274 	 * restore SUID shouldn't return a failure.
275 	 * We check below to make sure SUID really wasn't set.
276 	 * See more detailed comments below.
277 	 */
278 	failure("Opportunistic SUID failure shouldn't return error.");
279 	assertEqualInt(0, archive_write_finish_entry(a));
280 
281         if (getuid() != 0) {
282 		assert(archive_entry_clear(ae) != NULL);
283 		archive_entry_copy_pathname(ae, "file_bad_suid2");
284 		archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
285 		archive_entry_set_uid(ae, getuid() + 1);
286 		archive_write_disk_set_options(a,
287 		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
288 		assertA(0 == archive_write_header(a, ae));
289 		/* Owner change should fail here. */
290 		failure("Non-opportunistic SUID failure should return error.");
291 		assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a));
292 	}
293 
294 	/* Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit */
295 	assert(archive_entry_clear(ae) != NULL);
296 	archive_entry_copy_pathname(ae, "file_perm_sgid");
297 	archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
298 	archive_entry_set_gid(ae, defaultgid());
299 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
300 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
301 	failure("Setting SGID bit should succeed here.");
302 	assertEqualIntA(a, 0, archive_write_finish_entry(a));
303 
304 	if (altgid() == -1) {
305 		/*
306 		 * Current user must belong to at least two groups or
307 		 * else we can't test setting the GID to another group.
308 		 */
309 		skipping("Current user can't test gid restore: must belong to more than one group.");
310 	} else {
311 		/*
312 		 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit
313 		 * but without ARCHIVE_EXTRACT_OWNER.
314 		 */
315 		/*
316 		 * This is a weird case: The user has asked for permissions to
317 		 * be restored but not asked for ownership to be restored.  As
318 		 * a result, the default file creation will create a file with
319 		 * the wrong group.  There are several possible behaviors for
320 		 * libarchive in this scenario:
321 		 *  = Set the SGID bit.  It is wrong and a security hole to
322 		 *    set SGID with the wrong group.  Even POSIX thinks so.
323 		 *  = Implicitly set the group.  I don't like this.
324 		 *  = drop the SGID bit and warn (the old libarchive behavior)
325 		 *  = drop the SGID bit and don't warn (the current libarchive
326 		 *    behavior).
327 		 * The current behavior sees SGID/SUID restore when you
328 		 * don't ask for owner restore as an "opportunistic"
329 		 * action.  That is, libarchive should do it if it can,
330 		 * but if it can't, it's not an error.
331 		 */
332 		assert(archive_entry_clear(ae) != NULL);
333 		archive_entry_copy_pathname(ae, "file_alt_sgid");
334 		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
335 		archive_entry_set_uid(ae, getuid());
336 		archive_entry_set_gid(ae, altgid());
337 		archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
338 		assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
339 		failure("Setting SGID bit should fail because of group mismatch but the failure should be silent because we didn't ask for the group to be set.");
340 		assertEqualIntA(a, 0, archive_write_finish_entry(a));
341 
342 		/*
343 		 * As above, but add _EXTRACT_OWNER to verify that it
344 		 * does succeed.
345 		 */
346 		assert(archive_entry_clear(ae) != NULL);
347 		archive_entry_copy_pathname(ae, "file_alt_sgid_owner");
348 		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
349 		archive_entry_set_uid(ae, getuid());
350 		archive_entry_set_gid(ae, altgid());
351 		archive_write_disk_set_options(a,
352 		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
353 		assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
354 		failure("Setting SGID bit should succeed here.");
355 		assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
356 	}
357 
358 	/*
359 	 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit,
360 	 * but wrong GID.  POSIX says you shouldn't restore SGID bit
361 	 * unless the GID could be restored.
362 	 */
363 	if (invalidgid() == -1) {
364 		/* This test always fails for root. */
365 		printf("Running as root: Can't test SGID failures.\n");
366 	} else {
367 		assert(archive_entry_clear(ae) != NULL);
368 		archive_entry_copy_pathname(ae, "file_bad_sgid");
369 		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
370 		archive_entry_set_gid(ae, invalidgid());
371 		archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
372 		assertA(0 == archive_write_header(a, ae));
373 		failure("This SGID restore should fail without an error.");
374 		assertEqualIntA(a, 0, archive_write_finish_entry(a));
375 
376 		assert(archive_entry_clear(ae) != NULL);
377 		archive_entry_copy_pathname(ae, "file_bad_sgid2");
378 		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
379 		archive_entry_set_gid(ae, invalidgid());
380 		archive_write_disk_set_options(a,
381 		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
382 		assertA(0 == archive_write_header(a, ae));
383 		failure("This SGID restore should fail with an error.");
384 		assertEqualIntA(a, ARCHIVE_WARN, archive_write_finish_entry(a));
385 	}
386 
387 	/* Set ownership should fail if we're not root. */
388 	if (getuid() == 0) {
389 		printf("Running as root: Can't test setuid failures.\n");
390 	} else {
391 		assert(archive_entry_clear(ae) != NULL);
392 		archive_entry_copy_pathname(ae, "file_bad_owner");
393 		archive_entry_set_mode(ae, S_IFREG | 0744);
394 		archive_entry_set_uid(ae, getuid() + 1);
395 		archive_write_disk_set_options(a, ARCHIVE_EXTRACT_OWNER);
396 		assertA(0 == archive_write_header(a, ae));
397 		assertEqualIntA(a,ARCHIVE_WARN,archive_write_finish_entry(a));
398 	}
399 
400 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
401 	archive_entry_free(ae);
402 
403 	/* Test the entries on disk. */
404 	assertEqualInt(0, stat("file_0755", &st));
405 	failure("file_0755: st.st_mode=%o", st.st_mode);
406 	assertEqualInt(st.st_mode & 07777, 0755);
407 
408 	assertEqualInt(0, stat("file_overwrite_0144", &st));
409 	failure("file_overwrite_0144: st.st_mode=%o", st.st_mode);
410 	assertEqualInt(st.st_mode & 07777, 0144);
411 
412 	assertEqualInt(0, stat("dir_0514", &st));
413 	failure("dir_0514: st.st_mode=%o", st.st_mode);
414 	assertEqualInt(st.st_mode & 07777, 0514);
415 
416 	assertEqualInt(0, stat("dir_overwrite_0744", &st));
417 	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
418 	assertEqualInt(st.st_mode & 0777, 0744);
419 
420 	assertEqualInt(0, stat("file_no_suid", &st));
421 	failure("file_0755: st.st_mode=%o", st.st_mode);
422 	assertEqualInt(st.st_mode & 07777, 0755);
423 
424 	assertEqualInt(0, stat("file_0777", &st));
425 	failure("file_0777: st.st_mode=%o", st.st_mode);
426 	assertEqualInt(st.st_mode & 07777, 0777);
427 
428 	/* SUID bit should get set here. */
429 	assertEqualInt(0, stat("file_4742", &st));
430 	failure("file_4742: st.st_mode=%o", st.st_mode);
431 	assertEqualInt(st.st_mode & 07777, S_ISUID | 0742);
432 
433 	/* SUID bit should NOT have been set here. */
434 	assertEqualInt(0, stat("file_bad_suid", &st));
435 	failure("file_bad_suid: st.st_mode=%o", st.st_mode);
436 	assertEqualInt(st.st_mode & 07777, 0742);
437 
438 	/* Some things don't fail if you're root, so suppress this. */
439 	if (getuid() != 0) {
440 		/* SUID bit should NOT have been set here. */
441 		assertEqualInt(0, stat("file_bad_suid2", &st));
442 		failure("file_bad_suid2: st.st_mode=%o", st.st_mode);
443 		assertEqualInt(st.st_mode & 07777, 0742);
444 	}
445 
446 	/* SGID should be set here. */
447 	assertEqualInt(0, stat("file_perm_sgid", &st));
448 	failure("file_perm_sgid: st.st_mode=%o", st.st_mode);
449 	assertEqualInt(st.st_mode & 07777, S_ISGID | 0742);
450 
451 	if (altgid() != -1) {
452 		/* SGID should not be set here. */
453 		assertEqualInt(0, stat("file_alt_sgid", &st));
454 		failure("file_alt_sgid: st.st_mode=%o", st.st_mode);
455 		assertEqualInt(st.st_mode & 07777, 0742);
456 
457 		/* SGID should be set here. */
458 		assertEqualInt(0, stat("file_alt_sgid_owner", &st));
459 		failure("file_alt_sgid: st.st_mode=%o", st.st_mode);
460 		assertEqualInt(st.st_mode & 07777, S_ISGID | 0742);
461 	}
462 
463 	if (invalidgid() != -1) {
464 		/* SGID should NOT be set here. */
465 		assertEqualInt(0, stat("file_bad_sgid", &st));
466 		failure("file_bad_sgid: st.st_mode=%o", st.st_mode);
467 		assertEqualInt(st.st_mode & 07777, 0742);
468 		/* SGID should NOT be set here. */
469 		assertEqualInt(0, stat("file_bad_sgid2", &st));
470 		failure("file_bad_sgid2: st.st_mode=%o", st.st_mode);
471 		assertEqualInt(st.st_mode & 07777, 0742);
472 	}
473 
474 	if (getuid() != 0) {
475 		assertEqualInt(0, stat("file_bad_owner", &st));
476 		failure("file_bad_owner: st.st_mode=%o", st.st_mode);
477 		assertEqualInt(st.st_mode & 07777, 0744);
478 		failure("file_bad_owner: st.st_uid=%d getuid()=%d",
479 		    st.st_uid, getuid());
480 		/* The entry had getuid()+1, but because we're
481 		 * not root, we should not have been able to set that. */
482 		assertEqualInt(st.st_uid, getuid());
483 	}
484 #endif
485 }
486