1 /* test34: chmod() chown() Author: Jan-Mark Wams (jms@cs.vu.nl) */
3 /* There is a problem getting valid uids and gids, so we use the passwd
4 ** file (ie. /etc/passwd). I don't like this, but I see no other way.
5 ** The read-only-device-error (EROFS) is not checked!
6 ** Supplementary group IDs are ignored.
28 #define ALL_RWXB (S_IRWXU | S_IRWXG | S_IRWXO)
29 #define ALL_SETB (S_ISUID | S_ISGID)
30 #define ALL_BITS (ALL_RWXB | ALL_SETB)
32 #define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd)
33 #define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir)
34 #define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a)
35 #define Mkfifo(f) if (mkfifo(f,0777)!=0) printf("Can't make fifo %s\n", f)
36 #define Mkdir(f) if (mkdir(f,0777)!=0) printf("Can't make dir %s\n", f)
37 #define Creat(f) if (close(creat(f,0777))!=0) printf("Can't creat %s\n",f)
39 /* This program uses /etc/passwd and assumes things about it's contents. */
40 #define PASSWD_FILE "/etc/passwd"
44 char *MaxName
; /* Name of maximum length */
45 char MaxPath
[PATH_MAX
]; /* Same for path */
46 char *NameTooLong
; /* Name of maximum +1 length */
47 char PathTooLong
[PATH_MAX
+ 1]; /* Same for path, both too long */
52 mode_t
mode(char *file_name
);
53 void makelongnames(void);
54 void getids(uid_t
* uid
, gid_t
* gid
);
56 int main(int argc
, char *argv
[])
61 if (argc
== 2) m
= atoi(argv
[1]);
65 superuser
= (geteuid() == (uid_t
) 0);
67 #ifdef _POSIX_CHOWN_RESTRICTED
68 I_can_chown
= superuser
;
74 for (i
= 1; i
< ITERATIONS
; i
++) {
75 if (m
& 0001) test34a();
76 if (m
& 0002) test34b();
77 if (m
& 0004) test34c();
81 return(-1); /* Unreachable */
85 { /* Test normal operation. */
96 /* Make scratch file. */
99 for (mod
= 0; mod
<= ALL_BITS
; mod
++) {
100 if ((mod
& ALL_BITS
) != mod
) /* If not a valid mod next. */
103 if (time(&time1
) == (time_t) - 1) e(1);
104 if (chmod("foo", mod
) != 0) e(2);
106 if (time(&time2
) == (time_t) - 1) e(3);
108 if ((st2
.st_mode
& ALL_BITS
) != mod
) e(4);
110 if ((st2
.st_mode
& ALL_RWXB
) != (mod
& ALL_RWXB
)) e(5);
112 /* Test the C time feald. */
113 if (st1
.st_ctime
> st2
.st_ctime
) e(6);
114 if (st1
.st_ctime
> time1
) e(7);
115 if (st1
.st_ctime
> time2
) e(8);
116 #ifndef V1_FILESYSTEM
117 if (st2
.st_ctime
< time1
) e(9);
119 if (st2
.st_ctime
> time2
) e(10);
120 if (st1
.st_atime
!= st2
.st_atime
) e(11);
121 if (st1
.st_mtime
!= st2
.st_mtime
) e(12);
122 } /* End for loop. */
124 /* Check if chown(file, geteuid(), getegid()) works. */
125 for (cnt
= 0; cnt
< 20; cnt
++) {
126 /* Set all rights on foo, including the set .id bits. */
127 if (chmod("foo", ALL_BITS
) != 0) e(13);
129 if (time(&time1
) == (time_t) -1) e(14);
131 if (chown("foo", geteuid(), getegid()) != 0) e(15);
133 if (time(&time2
) == (time_t) -1) e(16);
135 /* Check ``chown()'' killed the set .id bits. */
137 if ((st1
.st_mode
& ALL_RWXB
) != ALL_RWXB
) e(17);
138 if ((st2
.st_mode
& ALL_BITS
) != ALL_RWXB
) e(18);
141 if ((st1
.st_mode
& ALL_BITS
) != ALL_BITS
) e(19);
142 if ((st1
.st_mode
& ALL_RWXB
) != ALL_RWXB
) e(20);
145 /* Check the timing. */
146 if (st1
.st_ctime
> st2
.st_ctime
) e(21);
147 if (st1
.st_ctime
> time1
) e(22);
148 if (st1
.st_ctime
> time2
) e(23);
149 #ifndef V1_FILESYSTEM
150 if (st2
.st_ctime
< time1
) e(24);
152 if (st2
.st_ctime
> time2
) e(25);
153 if (st1
.st_atime
!= st2
.st_atime
) e(26);
154 if (st1
.st_mtime
!= st2
.st_mtime
) e(27);
155 } /* End for loop. */
157 /* Make scratch file. */
158 if (chmod("foo", ALL_RWXB
) != 0) e(28);
161 /* Do a 20 tests on a gid and uid. */
162 for (cnt
= 0; cnt
< 20; cnt
++) {
163 /* Get a uid and a gid, test chown. */
166 if (time(&time1
) == (time_t) -1) e(29);
167 if (chown("foo", (uid_t
) 0, (gid_t
) 0) != 0) e(30);
169 if (time(&time2
) == (time_t) -1) e(31);
171 /* Test the C time field. */
172 if (st1
.st_ctime
> st2
.st_ctime
) e(32);
173 if (st1
.st_ctime
> time1
) e(33);
174 if (st1
.st_ctime
> time2
) e(34);
175 if (st2
.st_ctime
< time1
) e(35);
176 if (st2
.st_ctime
> time2
) e(36);
177 if (st1
.st_atime
!= st2
.st_atime
) e(37);
178 if (st1
.st_mtime
!= st2
.st_mtime
) e(38);
180 /* Do aditional tests. */
181 if (chown("foo", (uid_t
) 0, gid
) != 0) e(39);
182 if (chown("foo", uid
, (gid_t
) 0) != 0) e(40);
183 if (chown("foo", uid
, gid
) != 0) e(41);
187 /* Check if a non-superuser can change a files gid to gid2 *
188 * if gid2 is the current process gid. */
189 for (cnt
= 0; cnt
< 5; cnt
++) {
192 printf("Can't fork\n");
202 getids(&uid2
, &gid2
);
203 if (gid
== gid2
) e(43);
205 /* Creat boo and bar for user uid of group gid. */
207 if (chown("boo", uid
, gid
) != 0) e(44);
208 if (chmod("boo", ALL_BITS
) != 0) e(45);
210 if (chown("bar", uid
, gid
) != 0) e(46);
211 if (chmod("bar", ALL_BITS
) != 0) e(47);
213 /* We now become user uid of group gid2. */
218 if (time(&time1
) == (time_t) -1) e(48);
219 if (chown("bar", uid
, gid2
) != 0) e(49);
221 if (time(&time2
) == (time_t) -1) e(50);
223 /* Check if the SET_BITS are cleared. */
224 if ((st1
.st_mode
& ALL_BITS
) != ALL_BITS
) e(51);
225 if ((st2
.st_mode
& ALL_BITS
) != ALL_RWXB
) e(52);
227 /* Check the st_times. */
228 if (st1
.st_ctime
> st2
.st_ctime
) e(53);
229 if (st1
.st_ctime
> time1
) e(54);
230 if (st1
.st_ctime
> time2
) e(55);
231 if (st2
.st_ctime
< time1
) e(56);
232 if (st2
.st_ctime
> time2
) e(57);
233 if (st1
.st_atime
!= st2
.st_atime
) e(58);
234 if (st1
.st_mtime
!= st2
.st_mtime
) e(59);
237 if (chmod("boo", ALL_BITS
) != 0) e(60);
240 /* Check if the set gid bit is cleared. */
241 if ((st1
.st_mode
& ALL_RWXB
) != ALL_RWXB
) e(61);
242 if ((st2
.st_mode
& S_ISGID
) != 0) e(62);
244 if (chown("boo", uid
, gid2
) != 0) e(63);
247 /* Check if the set uid bit is cleared. */
248 if ((st1
.st_mode
& S_ISUID
) != 0) e(64);
253 if (stat_loc
!= 0) e(65); /* Alarm? */
255 } /* end for loop. */
256 } /* end if (superuser). */
257 if (chmod("foo", ALL_BITS
) != 0) e(66);
259 if (chown("foo", geteuid(), getegid()) != 0) e(67);
261 if ((st1
.st_mode
& ALL_BITS
) != ALL_BITS
) e(68); /* See intro! */
263 if ((st2
.st_mode
& ALL_RWXB
) != ALL_RWXB
) e(69);
265 if ((st2
.st_mode
& ALL_BITS
) != ALL_RWXB
) e(70);
267 (void) system("chmod 777 ../DIR_34/* > /dev/null 2> /dev/null");
268 System("rm -rf ../DIR_34/*");
275 struct stat st1
, st2
;
279 /* Test chmod() and chown() on non regular files and on MaxName and
280 * MaxPath. * Funny, but dirs should also have S_IS.ID bits.
285 MaxPath
[strlen(MaxPath
) - 2] = '/';
286 MaxPath
[strlen(MaxPath
) - 1] = 'a'; /* make ././.../a */
289 for (mod
= 1; mod
<= ALL_BITS
; mod
<<= 1) {
290 if ((mod
& ALL_BITS
) != mod
) continue; /* bad mod */
292 if (time(&time1
) == (time_t) -1) e(1);
293 if (chmod("dir", mod
) != 0) e(2);
295 if (time(&time2
) == (time_t) -1) e(3);
297 if ((st2
.st_mode
& ALL_BITS
) != mod
) e(4);
299 if ((st2
.st_mode
& ALL_RWXB
) != (mod
& ALL_RWXB
)) e(5);
301 /* Test the C time field. */
302 if (st1
.st_ctime
> st2
.st_ctime
) e(6);
303 if (st1
.st_ctime
> time1
) e(7);
304 if (st1
.st_ctime
> time2
) e(8);
305 #ifndef V1_FILESYSTEM
306 if (st2
.st_ctime
< time1
) e(9);
308 if (st2
.st_ctime
> time2
) e(10);
309 if (st1
.st_atime
!= st2
.st_atime
) e(11);
310 if (st1
.st_mtime
!= st2
.st_mtime
) e(12);
313 if (time(&time1
) == (time_t) -1) e(13);
314 if (chmod("fifo", mod
) != 0) e(14);
316 if (time(&time2
) == (time_t) -1) e(15);
318 if ((st2
.st_mode
& ALL_BITS
) != mod
) e(16);
320 if ((st2
.st_mode
& ALL_RWXB
) != (mod
& ALL_RWXB
)) e(17);
322 /* Test the C time field. */
323 if (st1
.st_ctime
> st2
.st_ctime
) e(18);
324 if (st1
.st_ctime
> time1
) e(19);
325 if (st1
.st_ctime
> time2
) e(20);
326 #ifndef V1_FILESYSTEM
327 if (st2
.st_ctime
< time1
) e(21);
329 if (st2
.st_ctime
> time2
) e(22);
330 if (st1
.st_atime
!= st2
.st_atime
) e(23);
331 if (st1
.st_mtime
!= st2
.st_mtime
) e(24);
334 if (time(&time1
) == (time_t) -1) e(25);
335 if (chmod(MaxName
, mod
) != 0) e(26);
337 if (time(&time2
) == (time_t) -1) e(27);
339 if ((st2
.st_mode
& ALL_BITS
) != mod
) e(28);
341 if ((st2
.st_mode
& ALL_RWXB
) != (mod
& ALL_RWXB
)) e(29);
343 /* Test the C time field. */
344 if (st1
.st_ctime
> st2
.st_ctime
) e(30);
345 if (st1
.st_ctime
> time1
) e(31);
346 if (st1
.st_ctime
> time2
) e(32);
347 #ifndef V1_FILESYSTEM
348 if (st2
.st_ctime
< time1
) e(33);
350 if (st2
.st_ctime
> time2
) e(34);
351 if (st1
.st_atime
!= st2
.st_atime
) e(35);
352 if (st1
.st_mtime
!= st2
.st_mtime
) e(36);
355 if (time(&time1
) == (time_t) -1) e(37);
356 if (chmod(MaxPath
, mod
) != 0) e(38);
358 if (time(&time2
) == (time_t) -1) e(39);
360 if ((st2
.st_mode
& ALL_BITS
) != mod
) e(40);
362 if ((st2
.st_mode
& ALL_RWXB
) != (mod
& ALL_RWXB
)) e(41);
364 /* Test the C time field. */
365 if (st1
.st_ctime
> st2
.st_ctime
) e(42);
366 if (st1
.st_ctime
> time1
) e(43);
367 if (st1
.st_ctime
> time2
) e(44);
368 #ifndef V1_FILESYSTEM
369 if (st2
.st_ctime
< time1
) e(45);
371 if (st2
.st_ctime
> time2
) e(46);
372 if (st1
.st_atime
!= st2
.st_atime
) e(47);
373 if (st1
.st_mtime
!= st2
.st_mtime
) e(48);
376 if (chmod("dir", 0777) != 0) e(49);
377 if (chmod("fifo", 0777) != 0) e(50);
378 if (chmod(MaxName
, 0777) != 0) e(51);
379 if (chmod(MaxPath
, 0777) != 0) e(52);
381 (void) system("chmod 777 ../DIR_34/* > /dev/null 2> /dev/null");
382 System("rm -rf ../DIR_34/*");
390 int fd
, does_truncate
, stat_loc
;
397 /* Disalow search permission and see if chmod() and chown() return
400 if (chmod("dir", ALL_BITS
& ~S_IXUSR
) != 0) e(1);
402 if (chmod("dir/try_me", 0) != -1) e(2);
403 if (errno
!= EACCES
) e(3);
405 if (chown("dir/try_me", geteuid(), getegid()) != -1) e(4);
406 if (errno
!= EACCES
) e(5);
412 if (chmod("fifo/try_me", 0) != -1) e(6);
413 if (errno
!= ENOTDIR
) e(7);
414 if (chown("fifo/try_me", geteuid(), getegid()) != -1) e(8);
415 if (errno
!= ENOTDIR
) e(9);
418 if (chmod("file/try_me", 0) != -1) e(10);
419 if (errno
!= ENOTDIR
) e(11);
420 if (chown("file/try_me", geteuid(), getegid()) != -1) e(12);
421 if (errno
!= ENOTDIR
) e(13);
423 /* Check empty path. */
424 if (chmod("", 0) != -1) e(14);
425 if (errno
!= ENOENT
) e(15);
426 if (chown("", geteuid(), getegid()) != -1) e(16);
427 if (errno
!= ENOENT
) e(17);
429 /* Check non existing file name. */
430 if (chmod("non_exist", 0) != -1) e(18);
431 if (errno
!= ENOENT
) e(19);
432 if (chown("non_exist", geteuid(), getegid()) != -1) e(20);
433 if (errno
!= ENOENT
) e(21);
435 /* Check what we get if we do not have permisson. */
438 if (st
.st_uid
== geteuid()) e(22);
440 /* First I had 0, I changed it to st.st_mode 8-). */
441 if (chmod("/", st
.st_mode
) != -1) e(23);
442 if (errno
!= EPERM
) e(24);
446 if (st
.st_uid
== geteuid()) e(25);
447 if (chown("/", geteuid(), getegid()) != -1) e(26);
448 if (errno
!= EPERM
) e(27);
451 /* If we are superuser, we can test all id combinations. */
454 case -1: printf("Can't fork\n"); break;
463 getids(&uid2
, &gid2
);
464 if (gid
== gid2
) e(29);
465 if (uid
== uid2
) e(30);
467 /* Creat boo, owned by root. */
469 if (chmod("boo", ALL_BITS
) != 0) e(31);
471 /* Creat boo for user uid2 of group gid2. */
473 if (chown("bar", uid2
, gid2
) != 0) e(32);
474 if (chmod("bar", ALL_BITS
) != 0) e(33);
476 /* Creat my_gid for user uid2 of group gid. */
478 if (chown("my_gid", uid2
, gid
) != 0) e(34);
479 if (chmod("my_gid", ALL_BITS
) != 0) e(35);
481 /* Creat my_uid for user uid of uid gid. */
483 if (chown("my_uid", uid
, gid
) != 0) e(36);
484 if (chmod("my_uid", ALL_BITS
) != 0) e(37);
486 /* We now become user uid of uid gid. */
490 if (chown("boo", uid
, gid
) != -1) e(38);
491 if (errno
!= EPERM
) e(39);
492 if (chown("bar", uid
, gid
) != -1) e(40);
493 if (errno
!= EPERM
) e(41);
494 if (chown("my_gid", uid
, gid
) != -1) e(42);
495 if (errno
!= EPERM
) e(43);
496 if (chown("my_uid", uid
, gid2
) != -1) e(44);
498 /* The EPERM is not strict POSIX. */
499 if (errno
!= EPERM
) e(45);
501 if (chmod("boo", 0) != -1) e(46);
502 if (errno
!= EPERM
) e(47);
503 if (chmod("bar", 0) != -1) e(48);
504 if (errno
!= EPERM
) e(49);
505 if (chmod("my_gid", 0) != -1) e(50);
506 if (errno
!= EPERM
) e(51);
511 if (stat_loc
!= 0) e(52); /* Alarm? */
515 /* Check too long path ed. */
516 does_truncate
= does_fs_truncate();
517 fd
= creat(NameTooLong
, 0777);
520 if (close(fd
) != 0) e(54);
521 if (chmod(NameTooLong
, 0777) != 0) e(55);
522 if (chown(NameTooLong
, geteuid(), getegid()) != 0) e(56);
525 if (errno
!= ENAMETOOLONG
) e(58);
526 (void) close(fd
); /* Just in case */
529 /* Make PathTooLong contain ././.../a */
530 PathTooLong
[strlen(PathTooLong
) - 2] = '/';
531 PathTooLong
[strlen(PathTooLong
) - 1] = 'a';
533 if (chmod(PathTooLong
, 0777) != -1) e(59);
534 if (errno
!= ENAMETOOLONG
) e(60);
535 if (chown(PathTooLong
, geteuid(), getegid()) != -1) e(61);
536 if (errno
!= ENAMETOOLONG
) e(62);
538 (void) system("chmod 777 ../DIR_34/* > /dev/null 2> /dev/null");
539 System("rm -rf ../DIR_34/*");
547 max_name_length
= name_max("."); /* Aka NAME_MAX, but not every FS supports
548 * the same length, hence runtime check */
549 MaxName
= malloc(max_name_length
+ 1);
550 NameTooLong
= malloc(max_name_length
+ 1 + 1); /* Name of maximum +1 length */
551 memset(MaxName
, 'a', max_name_length
);
552 MaxName
[max_name_length
] = '\0';
554 for (i
= 0; i
< PATH_MAX
- 1; i
++) { /* idem path */
558 MaxPath
[PATH_MAX
- 1] = '\0';
560 strcpy(NameTooLong
, MaxName
); /* copy them Max to ToLong */
561 strcpy(PathTooLong
, MaxPath
);
563 NameTooLong
[max_name_length
] = 'a';
564 NameTooLong
[max_name_length
+1] = '\0';/* extend ToLongName by one too many */
565 PathTooLong
[PATH_MAX
- 1] = '/';
566 PathTooLong
[PATH_MAX
] = '\0'; /* inc ToLongPath by one */
569 /* Getids returns a valid uid and gid. Is used PASSWD FILE.
570 * It assumes the following format for a passwd file line:
571 * <user_name>:<passwd>:<uid>:<gid>:<other_stuff>
572 * If no uids and gids can be found, it will only return 0 ids.
574 void getids(r_uid
, r_gid
)
585 static uid_t a_uid
[N
]; /* Array for uids. */
586 static gid_t a_gid
[N
]; /* Array for gids. */
587 static int nuid
= 0, ngid
= 0;/* The number of user & group ids. */
588 static int cuid
= 0, cgid
= 0;/* The current id index. */
590 /* If we don't have any uids go read some from the passwd file. */
592 a_uid
[nuid
++] = 0; /* Root uid and gid. */
594 if ((fp
= fopen(PASSWD_FILE
, "r")) == NULL
) {
595 printf("Can't open ");
598 while (fp
!= NULL
&& fgets(line
, sizeof(line
), fp
) != NULL
) {
599 p
= strchr(line
, ':');
600 if (p
!= NULL
) p
= strchr(p
+ 1, ':');
604 while (isdigit(*p
)) {
606 uid
+= (uid_t
) (*p
- '0');
609 if (*p
!= ':') continue;
612 while (isdigit(*p
)) {
614 gid
+= (gid_t
) (*p
- '0');
617 if (*p
!= ':') continue;
619 for (i
= 0; i
< nuid
; i
++)
620 if (a_uid
[i
] == uid
) break;
621 if (i
== nuid
) a_uid
[nuid
++] = uid
;
624 for (i
= 0; i
< ngid
; i
++)
625 if (a_gid
[i
] == gid
) break;
626 if (i
== ngid
) a_gid
[ngid
++] = gid
;
628 if (nuid
>= N
&& ngid
>= N
) break;
631 if (fp
!= NULL
) fclose(fp
);
634 /* We now have uids and gids in a_uid and a_gid. */
635 if (cuid
>= nuid
) cuid
= 0;
636 if (cgid
>= ngid
) cgid
= 0;
637 *r_uid
= a_uid
[cuid
++];
638 *r_gid
= a_gid
[cgid
++];