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