1 /* 2 * Copyright (c) International Business Machines Corp., 2001 3 * 07/2001 Ported by Wayne Boyer 4 * Copyright (c) 2014 Cyril Hrubis <chrubis (at) suse.cz> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 14 * the GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 /* 22 * Test Description: 23 * Verify that, when fchown(2) invoked by super-user to change the owner and 24 * group of a file specified by file descriptor to any numeric 25 * owner(uid)/group(gid) values, 26 * - clears setuid and setgid bits set on an executable file. 27 * - preserves setgid bit set on a non-group-executable file. 28 */ 29 30 #include <stdio.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <sys/fcntl.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <signal.h> 37 38 #include "test.h" 39 #include "safe_macros.h" 40 #include "compat_16.h" 41 42 #define FILE_MODE S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 43 #define NEW_PERMS1 S_IFREG | S_IRWXU | S_IRWXG | S_ISUID | S_ISGID 44 #define NEW_PERMS2 S_IFREG | S_IRWXU | S_ISGID 45 #define EXP_PERMS S_IFREG | S_IRWXU | S_IRWXG 46 #define TESTFILE1 "testfile1" 47 #define TESTFILE2 "testfile2" 48 49 TCID_DEFINE(fchown02); 50 int TST_TOTAL = 2; 51 static int fd1; 52 static int fd2; 53 54 struct test_case { 55 int *fd; 56 char *pathname; 57 char *desc; 58 uid_t user_id; 59 gid_t group_id; 60 int test_flag; 61 } tc[] = { 62 {&fd1, TESTFILE1, "Setuid/Setgid bits cleared", 700, 701, 1}, 63 {&fd2, TESTFILE2, "Setgid bit not cleared", 700, 701, 2}, 64 {0, NULL, NULL, 0, 0, 0} 65 }; 66 67 static void setup(void); 68 static void cleanup(void); 69 70 static void verify_fchown(struct test_case *t) 71 { 72 struct stat stat_buf; 73 74 TEST(FCHOWN(cleanup, *t->fd, t->user_id, t->group_id)); 75 76 if (TEST_RETURN == -1) { 77 tst_resm(TFAIL | TTERRNO, "fchown() Fails on %s", t->pathname); 78 return; 79 } 80 81 SAFE_FSTAT(cleanup, *t->fd, &stat_buf); 82 83 if ((stat_buf.st_uid != t->user_id) || 84 (stat_buf.st_gid != t->group_id)) { 85 tst_resm(TFAIL, 86 "%s: Incorrect ownership expected %d %d, have %d %d", 87 t->pathname, t->user_id, t->group_id, 88 stat_buf.st_uid, stat_buf.st_gid); 89 } 90 91 switch (t->test_flag) { 92 case 1: 93 if (((stat_buf.st_mode & (S_ISUID | S_ISGID)))) { 94 tst_resm(TFAIL, "%s: Incorrect mode " 95 "permissions %#o, Expected " 96 "%#o", t->pathname, NEW_PERMS1, 97 EXP_PERMS); 98 return; 99 } 100 break; 101 case 2: 102 if ((!(stat_buf.st_mode & S_ISGID))) { 103 tst_resm(TFAIL, 104 "%s: Incorrect mode " 105 "permissions %#o, Expected " 106 "%#o", t->pathname, 107 stat_buf.st_mode, NEW_PERMS2); 108 return; 109 } 110 break; 111 } 112 113 tst_resm(TPASS, "fchown() on %s succeeds : %s", t->pathname, t->desc); 114 } 115 116 int main(int ac, char **av) 117 { 118 int lc, i; 119 120 tst_parse_opts(ac, av, NULL, NULL); 121 122 setup(); 123 124 for (lc = 0; TEST_LOOPING(lc); lc++) { 125 for (i = 0; tc[i].desc != NULL; i++) 126 verify_fchown(tc + i); 127 } 128 129 cleanup(); 130 tst_exit(); 131 } 132 133 static void setup(void) 134 { 135 tst_require_root(); 136 137 tst_sig(NOFORK, DEF_HANDLER, cleanup); 138 139 TEST_PAUSE; 140 141 tst_tmpdir(); 142 143 fd1 = SAFE_OPEN(cleanup, TESTFILE1, O_RDWR | O_CREAT, FILE_MODE); 144 SAFE_CHMOD(cleanup, TESTFILE1, NEW_PERMS1); 145 fd2 = SAFE_OPEN(cleanup, TESTFILE2, O_RDWR | O_CREAT, FILE_MODE); 146 SAFE_CHMOD(cleanup, TESTFILE2, NEW_PERMS2); 147 } 148 149 static void cleanup(void) 150 { 151 if (fd1 > 0 && close(fd1)) 152 tst_resm(TWARN | TERRNO, "Failed to close fd1"); 153 154 if (fd2 > 0 && close(fd2)) 155 tst_resm(TWARN | TERRNO, "Failed to close fd2"); 156 157 tst_rmdir(); 158 } 159