The Pedigree Project 0.1
file-lock-contract-test/creation.c
1#define _GNU_SOURCE
2#include <grp.h>
3#include <signal.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/stat.h>
8
9static int metadata_matches(const char* name, const struct stat* metadata, mode_t mode, uid_t uid,
10 gid_t gid) {
11 if ((metadata->st_mode & 0777) == mode && metadata->st_uid == uid && metadata->st_gid == gid)
12 return 1;
13 fprintf(stderr,
14 "FILE-LOCK-CONTRACT: %s mode=%04o uid=%lu gid=%lu expected mode=%04o uid=%lu gid=%lu\n",
15 name, (unsigned)(metadata->st_mode & 07777), (unsigned long)metadata->st_uid,
16 (unsigned long)metadata->st_gid, (unsigned)mode, (unsigned long)uid, (unsigned long)gid);
17 return 0;
18}
19
20int file_lock_creation(void) {
21 int failed = 0, fd = -1;
22 pid_t child = -1;
23 char file[128], directory[128], owned_file[128], owned_directory[128];
24 struct stat metadata;
25 const mode_t saved = umask(0027);
26 snprintf(file, sizeof(file), "/tmp/file-lock-mode-%ld", (long)getpid());
27 snprintf(directory, sizeof(directory), "/tmp/file-lock-dir-%ld", (long)getpid());
28 snprintf(owned_file, sizeof(owned_file), "/tmp/file-lock-owned-%ld", (long)getpid());
29 snprintf(owned_directory, sizeof(owned_directory), "/tmp/file-lock-own-dir-%ld", (long)getpid());
30 CHECK((fd = open(file, O_CREAT | O_EXCL | O_RDWR, 0666)) >= 0);
31 CHECK(fstat(fd, &metadata) == 0 && metadata_matches(file, &metadata, 0640, geteuid(), getegid()));
32 CHECK(mkdir(directory, 0777) == 0);
33 CHECK(stat(directory, &metadata) == 0 &&
34 metadata_matches(directory, &metadata, 0750, geteuid(), getegid()));
35 if (!geteuid()) {
36 CHECK((child = fork()) >= 0);
37 if (!child) {
38 alarm(6);
39 if (setgroups(0, NULL) || setgid(65534) || setuid(65534))
40 _exit(10);
41 if (open(file, O_RDONLY) != -1 || errno != EACCES ||
42 open(directory, O_RDONLY | O_DIRECTORY) != -1 || errno != EACCES)
43 _exit(11);
44 int own = open(owned_file, O_CREAT | O_EXCL | O_RDWR, 0666);
45 if (own < 0 || fstat(own, &metadata) ||
46 !metadata_matches(owned_file, &metadata, 0640, 65534, 65534))
47 _exit(12);
48 close(own);
49 own = open(owned_file, O_RDWR);
50 if (own < 0 || write(own, "x", 1) != 1 || mkdir(owned_directory, 0777) ||
51 stat(owned_directory, &metadata) ||
52 !metadata_matches(owned_directory, &metadata, 0750, 65534, 65534))
53 _exit(13);
54 close(own);
55 own = open(owned_directory, O_RDONLY | O_DIRECTORY);
56 if (own < 0)
57 _exit(14);
58 close(own);
59 if (unlink(owned_file) || rmdir(owned_directory))
60 _exit(15);
61 _exit(0);
62 }
63 CHECK(fl_reap(child, 7000) == 0);
64 child = -1;
65 } else {
66 puts("FILE-LOCK-CONTRACT: SKIP nonowner creation checks require root");
67 }
68out:
69 if (child > 0) {
70 kill(child, SIGKILL);
71 fl_reap(child, 1000);
72 }
73 umask(saved);
74 if (fd >= 0)
75 close(fd);
76 unlink(file);
77 rmdir(directory);
78 unlink(owned_file);
79 rmdir(owned_directory);
80 return failed;
81}