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
searchgid(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
altgid(void)98 altgid(void)
99 {
100 searchgid();
101 return (_alt_gid);
102 }
103
104 static int
invalidgid(void)105 invalidgid(void)
106 {
107 searchgid();
108 return (_invalid_gid);
109 }
110
111 static int
defaultgid(void)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
DEFINE_TEST(test_write_disk_perms)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