The Pedigree Project 0.1
xattr-contract-test/admission.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/eventfd.h>
8#include <sys/mman.h>
9#include <sys/stat.h>
10#include <sys/xattr.h>
11
12static int names_and_faults(void) {
13 int failed = 0, path_fd = -1, anonymous = -1;
14 struct xa_file file = {.fd = -1};
15 void* bad = MAP_FAILED;
16 char name[257], missing[224], nondirectory[224];
17 CHECK(!xa_create(&file, XA_RAMFS, 0));
18 CHECK(!fsetxattr(file.fd, "user.keep", "k", 1, 0));
19 bad = mmap(NULL, xa_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
20 CHECK(bad != MAP_FAILED);
21 CHECK(fsetxattr(file.fd, "", "v", 1, 0) == -1 && errno == ERANGE);
22 CHECK(fgetxattr(file.fd, "", NULL, 0) == -1 && errno == ERANGE);
23 CHECK(fremovexattr(file.fd, "") == -1 && errno == ERANGE);
24 CHECK(fsetxattr(file.fd, "user.", "v", 1, 0) == -1 && errno == EINVAL);
25 memcpy(name, "user.", 5);
26 memset(name + 5, 'n', 250);
27 name[255] = 0;
28 CHECK(!fsetxattr(file.fd, name, "v", 1, 0));
29 CHECK(!xa_value(&file, XA_FD, name, "v", 1));
30 CHECK(!fremovexattr(file.fd, name));
31 name[255] = 'n';
32 name[256] = 0;
33 CHECK(fsetxattr(file.fd, name, "v", 1, 0) == -1 && errno == ERANGE);
34 CHECK(fgetxattr(file.fd, name, NULL, 0) == -1 && errno == ERANGE);
35 CHECK(fremovexattr(file.fd, name) == -1 && errno == ERANGE);
36 const char high_name[] = {'u', 's', 'e', 'r', '.', (char)0x80, (char)0xff, 0};
37 CHECK(!fsetxattr(file.fd, high_name, "b", 1, 0));
38 CHECK(!xa_value(&file, XA_FD, high_name, "b", 1));
39 CHECK(!fremovexattr(file.fd, high_name));
40 CHECK(!fsetxattr(file.fd, "user.zero\0ignored", "z", 1, 0));
41 CHECK(!xa_value(&file, XA_FD, "user.zero", "z", 1));
42 CHECK(fgetxattr(file.fd, "user.Zero", NULL, 0) == -1 && errno == ENODATA);
43 CHECK(!fremovexattr(file.fd, "user.zero"));
44 const char* unsupported[] = {"trusted.test", "security.test", "system.posix_acl_access",
45 "unknown.test", "User.test"};
46 for (size_t n = 0; n < sizeof(unsupported) / sizeof(unsupported[0]); ++n) {
47 CHECK(fsetxattr(file.fd, unsupported[n], "v", 1, 0) == -1 && errno == EOPNOTSUPP);
48 CHECK(fgetxattr(file.fd, unsupported[n], NULL, 0) == -1 && errno == EOPNOTSUPP);
49 CHECK(fremovexattr(file.fd, unsupported[n]) == -1 && errno == EOPNOTSUPP);
50 }
51 const char* keep[] = {"user.keep"};
52 CHECK(!xa_names(&file, XA_FD, keep, 1));
53 CHECK(fsetxattr(-1, bad, bad, 1, 4) == -1 && errno == EBADF);
54 CHECK(fgetxattr(-1, bad, bad, 1) == -1 && errno == EBADF);
55 CHECK(flistxattr(-1, bad, 1) == -1 && errno == EBADF);
56 CHECK(fremovexattr(-1, bad) == -1 && errno == EBADF);
57 CHECK(fsetxattr(file.fd, bad, "v", 1, 0) == -1 && errno == EFAULT);
58 CHECK(fgetxattr(file.fd, bad, NULL, 0) == -1 && errno == EFAULT);
59 CHECK(fremovexattr(file.fd, bad) == -1 && errno == EFAULT);
60 snprintf(missing, sizeof(missing), "%s.missing", file.path);
61 CHECK(setxattr(missing, bad, bad, 1, 4) == -1 && errno == EINVAL);
62 CHECK(setxattr(missing, "user.x", bad, 1, 0) == -1 && errno == EFAULT);
63 CHECK(getxattr(missing, bad, NULL, 0) == -1 && errno == ENOENT);
64 CHECK(removexattr(missing, "") == -1 && errno == ERANGE);
65 CHECK(removexattr(missing, "user.x") == -1 && errno == ENOENT);
66 snprintf(nondirectory, sizeof(nondirectory), "%s/child", file.path);
67 CHECK(getxattr(nondirectory, "user.x", NULL, 0) == -1 && errno == ENOTDIR);
68 snprintf(nondirectory, sizeof(nondirectory), "%s/", file.path);
69 CHECK(lgetxattr(nondirectory, "user.keep", NULL, 0) == -1 && errno == ENOTDIR);
70 CHECK(lsetxattr(nondirectory, "user.keep", "x", 1, 0) == -1 && errno == ENOTDIR);
71 CHECK((path_fd = open(file.path, O_PATH | O_CLOEXEC)) >= 0);
72 CHECK(fsetxattr(path_fd, "user.x", "x", 1, 0) == -1 && errno == EBADF);
73 CHECK(fgetxattr(path_fd, "user.keep", NULL, 0) == -1 && errno == EBADF);
74 CHECK(flistxattr(path_fd, NULL, 0) == -1 && errno == EBADF);
75 CHECK(fremovexattr(path_fd, "user.keep") == -1 && errno == EBADF);
76 CHECK((anonymous = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) >= 0);
77 CHECK(fsetxattr(anonymous, "user.x", "x", 1, 0) == -1 && errno == EOPNOTSUPP);
78 CHECK(fgetxattr(anonymous, "user.x", NULL, 0) == -1 && errno == EOPNOTSUPP);
79 CHECK(flistxattr(anonymous, NULL, 0) == -1 && errno == EOPNOTSUPP);
80 CHECK(fremovexattr(anonymous, "user.x") == -1 && errno == EOPNOTSUPP);
81 CHECK(!xa_value(&file, XA_PATH, "user.keep", "k", 1));
82out:
83 if (anonymous >= 0)
84 close(anonymous);
85 if (path_fd >= 0)
86 close(path_fd);
87 if (bad != MAP_FAILED)
88 munmap(bad, xa_page);
89 xa_close(&file);
90 return failed;
91}
92
93static int link_resolution(void) {
94 int failed = 0;
95 struct xa_file file = {.fd = -1}, directory = {.fd = -1};
96 char link_path[224] = {0}, loop_path[224] = {0}, intermediate[448], parent_link[224] = {0};
97 char directory_link[224] = {0}, trailing[256];
98 CHECK(!xa_create(&file, XA_EXT2, 0));
99 CHECK(!fsetxattr(file.fd, "user.key", "a", 1, 0));
100 snprintf(link_path, sizeof(link_path), "%s.link", file.path);
101 snprintf(loop_path, sizeof(loop_path), "%s.loop", file.path);
102 snprintf(parent_link, sizeof(parent_link), "%s.parent", file.path);
103 CHECK(!symlink(file.path + 1, link_path));
104 CHECK(!symlink(loop_path + 1, loop_path));
105 CHECK(!symlink(".", parent_link));
106 CHECK(!chdir("/"));
107 char value = 0;
108 CHECK(getxattr(link_path, "user.key", &value, 1) == 1 && value == 'a');
109 CHECK(!setxattr(link_path, "user.key", "b", 1, XATTR_REPLACE));
110 CHECK(!xa_value(&file, XA_FD, "user.key", "b", 1));
111 CHECK(lgetxattr(link_path, "user.key", NULL, 0) == -1 && errno == ENODATA);
112 CHECK(lsetxattr(link_path, "user.key", "x", 1, 0) == -1 && errno == EPERM);
113 CHECK(lremovexattr(link_path, "user.key") == -1 && errno == EPERM);
114 CHECK(llistxattr(link_path, NULL, 0) == 0);
115 CHECK(listxattr(link_path, NULL, 0) == (ssize_t)sizeof("user.key"));
116 snprintf(intermediate, sizeof(intermediate), "%s%s", parent_link, file.path);
117 CHECK(!lsetxattr(intermediate, "user.key", "c", 1, 0));
118 CHECK(lgetxattr(file.path + 1, "user.key", &value, 1) == 1 && value == 'c');
119 CHECK(!xa_create(&directory, XA_EXT2, 1));
120 snprintf(directory_link, sizeof(directory_link), "%s.link", directory.path);
121 CHECK(!symlink(directory.path + 1, directory_link));
122 snprintf(trailing, sizeof(trailing), "%s/", directory_link);
123 CHECK(!lsetxattr(trailing, "user.directory", "d", 1, 0));
124 CHECK(lgetxattr(trailing, "user.directory", &value, 1) == 1 && value == 'd');
125 CHECK(llistxattr(trailing, NULL, 0) == (ssize_t)sizeof("user.directory"));
126 CHECK(!lremovexattr(trailing, "user.directory"));
127 CHECK(!removexattr(link_path, "user.key"));
128 CHECK(fgetxattr(file.fd, "user.key", NULL, 0) == -1 && errno == ENODATA);
129 CHECK(getxattr(loop_path, "user.key", NULL, 0) == -1 && errno == ELOOP);
130 CHECK(!unlink(file.path));
131 file.path[0] = 0;
132 CHECK(getxattr(link_path, "user.key", NULL, 0) == -1 && errno == ENOENT);
133 CHECK(lgetxattr(link_path, "user.key", NULL, 0) == -1 && errno == ENODATA);
134out:
135 if (directory_link[0])
136 unlink(directory_link);
137 xa_close(&directory);
138 if (parent_link[0])
139 unlink(parent_link);
140 if (loop_path[0])
141 unlink(loop_path);
142 if (link_path[0])
143 unlink(link_path);
144 xa_close(&file);
145 return failed;
146}
147
148int xa_admission(void) {
149 return names_and_faults() || link_resolution();
150}