1 /* Test for chmod functions.
2    Copyright (C) 2000-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <dirent.h>
20 #include <errno.h>
21 #include <error.h>
22 #include <fcntl.h>
23 #include <mcheck.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 
30 
31 #define OUT_OF_MEMORY \
32   do {									      \
33     puts ("cannot allocate memory");					      \
34     result = 1;								      \
35     goto fail;								      \
36   } while (0)
37 
38 static int
do_test(int argc,char * argv[])39 do_test (int argc, char *argv[])
40 {
41   const char *builddir;
42   struct stat64 st1;
43   struct stat64 st2;
44   char *buf;
45   char *testdir;
46   char *testfile = NULL;
47   char *startdir;
48   size_t buflen;
49   int fd;
50   int result = 0;
51   DIR *dir;
52 
53   mtrace ();
54 
55   if (argc <= 1)
56     error (EXIT_FAILURE, 0, "no parameters");
57 
58   /* This is where we will create the test files.  */
59   builddir = argv[1];
60   buflen = strlen (builddir) + 50;
61 
62   startdir = getcwd (NULL, 0);
63   if (startdir == NULL)
64     {
65       printf ("cannot get current directory: %m\n");
66       exit (EXIT_FAILURE);
67     }
68 
69   /* A buffer large enough for everything we need.  */
70   buf = (char *) alloca (buflen);
71 
72   /* Create the directory name.  */
73   snprintf (buf, buflen, "%s/chmoddirXXXXXX", builddir);
74 
75   if (mkdtemp (buf) == NULL)
76     {
77       printf ("cannot create test directory: %m\n");
78       exit (EXIT_FAILURE);
79     }
80 
81   if (chmod ("", 0600) == 0)
82     {
83       puts ("chmod(\"\", 0600 didn't fail");
84       result = 1;
85     }
86   else if (errno != ENOENT)
87     {
88       puts ("chmod(\"\",0600) does not set errno to ENOENT");
89       result = 1;
90     }
91 
92   /* Create a duplicate.  */
93   testdir = strdup (buf);
94   if (testdir == NULL)
95     OUT_OF_MEMORY;
96 
97   if (stat64 (testdir, &st1) != 0)
98     {
99       printf ("cannot stat test directory: %m\n");
100       exit (1);
101     }
102   if (!S_ISDIR (st1.st_mode))
103     {
104       printf ("file not created as directory: %m\n");
105       exit (1);
106     }
107 
108   /* We have to wait for a second to make sure the ctime changes.  */
109   sleep (1);
110 
111   /* Remove all access rights from the directory.  */
112   if (chmod (testdir, 0) != 0)
113     {
114       printf ("cannot change mode of test directory: %m\n");
115       result = 1;
116       goto fail;
117     }
118 
119   if (stat64 (testdir, &st2) != 0)
120     {
121       printf ("cannot stat test directory: %m\n");
122       result = 1;
123       goto fail;
124     }
125 
126   /* Compare result.  */
127   if ((st2.st_mode & ALLPERMS) != 0)
128     {
129       printf ("chmod(...,0) on directory left bits nonzero: %o\n",
130 	      st2.st_mode & ALLPERMS);
131       result = 1;
132     }
133   if (st1.st_ctime >= st2.st_ctime)
134     {
135       puts ("chmod(...,0) did not set ctime correctly");
136       result = 1;
137     }
138 
139   /* Name of a file in the directory.  */
140   snprintf (buf, buflen, "%s/file", testdir);
141   testfile = strdup (buf);
142   if (testfile == NULL)
143     OUT_OF_MEMORY;
144 
145   fd = creat (testfile, 0);
146   if (fd != -1)
147     {
148       if (getuid () != 0)
149 	{
150 	  puts ("managed to create test file in protected directory");
151 	  result = 1;
152 	}
153       close (fd);
154     }
155   else if (errno != EACCES)
156     {
157       puts ("creat didn't generate correct errno value");
158       result = 1;
159     }
160 
161   /* With this mode it still shouldn't be possible to create a file.  */
162   if (chmod (testdir, 0600) != 0)
163     {
164       printf ("cannot change mode of test directory to 0600: %m\n");
165       result = 1;
166       goto fail;
167     }
168 
169   fd = creat (testfile, 0);
170   if (fd != -1)
171     {
172       if (getuid () != 0)
173 	{
174 	  puts ("managed to create test file in no-x protected directory");
175 	  result = 1;
176 	}
177       close (fd);
178     }
179   else if (errno != EACCES)
180     {
181       puts ("creat didn't generate correct errno value");
182       result = 1;
183     }
184 
185   /* Change the directory mode back to allow creating a file.  This
186      time with fchmod.  */
187   dir = opendir (testdir);
188   if (dir != NULL)
189     {
190       if (fchmod (dirfd (dir), 0700) != 0)
191 	{
192 	  printf ("cannot change mode of test directory to 0700: %m\n");
193 	  result = 1;
194 	  closedir (dir);
195 	  goto fail;
196 	}
197 
198       closedir (dir);
199     }
200   else
201     {
202       printf ("cannot open directory: %m\n");
203       result = 1;
204 
205       if (chmod (testdir, 0700) != 0)
206 	{
207 	  printf ("cannot change mode of test directory to 0700: %m\n");
208 	  goto fail;
209 	}
210     }
211 
212   fd = creat (testfile, 0);
213   if (fd == -1)
214     {
215       puts ("still didn't manage to create test file in protected directory");
216       result = 1;
217       goto fail;
218     }
219   if (fstat64 (fd, &st1) != 0)
220     {
221       printf ("cannot stat new file: %m\n");
222       result = 1;
223     }
224   else if ((st1.st_mode & ALLPERMS) != 0)
225     {
226       puts ("file not created with access mode 0");
227       result = 1;
228     }
229   close (fd);
230 
231   snprintf (buf, buflen, "%s/..", testdir);
232   chdir (buf);
233   /* We are now in the directory above the one we create the test
234      directory in.  */
235 
236   sleep (1);
237   snprintf (buf, buflen, "./%s/../%s/file",
238 	    basename (testdir), basename (testdir));
239   if (chmod (buf, 0600) != 0)
240     {
241       printf ("cannot change mode of file to 0600: %m\n");
242       result = 1;
243       goto fail;
244     }
245   snprintf (buf, buflen, "./%s//file", basename (testdir));
246   if (stat64 (buf, &st2) != 0)
247     {
248       printf ("cannot stat new file: %m\n");
249       result = 1;
250     }
251   else if ((st2.st_mode & ALLPERMS) != 0600)
252     {
253       puts ("file mode not changed to 0600");
254       result = 1;
255     }
256   else if (st1.st_ctime >= st2.st_ctime)
257     {
258       puts ("chmod(\".../file\",0600) did not set ctime correctly");
259       result = 1;
260     }
261 
262   if (chmod (buf, 0777 | S_ISUID | S_ISGID) != 0)
263     {
264       printf ("cannot change mode of file to %o: %m\n",
265 	      0777 | S_ISUID | S_ISGID);
266       result = 1;
267     }
268   if (stat64 (buf, &st2) != 0)
269     {
270       printf ("cannot stat test file: %m\n");
271       result = 1;
272     }
273   else if ((st2.st_mode & ALLPERMS) != (0777 | S_ISUID | S_ISGID))
274     {
275       puts ("file mode not changed to 0777 | S_ISUID | S_ISGID");
276       result = 1;
277     }
278 
279   if (chmod (basename (testdir), 0777 | S_ISUID | S_ISGID | S_ISVTX) != 0)
280     {
281       printf ("cannot change mode of test directory to %o: %m\n",
282 	      0777 | S_ISUID | S_ISGID | S_ISVTX);
283       result = 1;
284     }
285   if (stat64 (basename (testdir), &st2) != 0)
286     {
287       printf ("cannot stat test directory: %m\n");
288       result = 1;
289     }
290   else if ((st2.st_mode & ALLPERMS) != (0777 | S_ISUID | S_ISGID | S_ISVTX))
291     {
292       puts ("directory mode not changed to 0777 | S_ISUID | S_ISGID | S_ISGID");
293       result = 1;
294     }
295 
296   snprintf (buf, buflen, "./%s/no-such-file", basename (testdir));
297   if (chmod (buf, 0600) != -1)
298     {
299       puts ("chmod(\".../no-such-file\",0600) did not fail");
300       result = 1;
301     }
302   else if (errno != ENOENT)
303     {
304       puts ("chmod(\".../no-such-file\",0600) does not set errno to ENOENT");
305       result = 1;
306     }
307 
308   snprintf (buf, buflen, "%s/", basename (testdir));
309   if (chmod (basename (testdir), 0677) != 0)
310     {
311       printf ("cannot change mode of test directory to 0677: %m\n");
312       result = 1;
313     }
314   else
315     {
316       snprintf (buf, buflen, "./%s/file", basename (testdir));
317       if (chmod (buf, 0600) == 0)
318 	{
319 	  if (getuid () != 0)
320 	    {
321 	      puts ("chmod(\".../file\") with no-exec directory succeeded");
322 	      result = 1;
323 	    }
324 	}
325       else if (errno != EACCES)
326 	{
327 	  puts ("chmod(\".../file\") with no-exec directory didn't set EACCES");
328 	  result = 1;
329 	}
330     }
331 
332   if (chmod (basename (testdir), 0777) != 0)
333     {
334       printf ("cannot change mode of test directory to 0777: %m\n");
335       result = 1;
336       goto fail;
337     }
338 
339   snprintf (buf, buflen, "%s/file/cannot-be", basename (testdir));
340   if (chmod (buf, 0600) == 0)
341     {
342       puts ("chmod(\".../file/cannot-be\",0600) did not fail");
343       result = 1;
344     }
345   else if (errno != ENOTDIR)
346     {
347       puts ("chmod(\".../file/cannot-be\",0600) does not set errno to ENOTDIR");
348       result = 1;
349     }
350 
351  fail:
352   chdir (startdir);
353 
354   /* Remove all the files.  */
355   chmod (testdir, 0700);
356   if (testfile != NULL)
357     {
358       chmod (testfile, 0700);
359       unlink (testfile);
360     }
361   rmdir (testdir);
362 
363   /* Free the resources.  */
364   free (testfile);
365   free (testdir);
366   free (startdir);
367 
368   return result;
369 }
370 
371 #include "../test-skeleton.c"
372