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