]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/test/test_write_disk_perms.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / test / test_write_disk_perms.c
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 }