python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / security / wrappers / wrapper.c
bloba21ec500208dfa85245821ccd6f19f3bdb838a01
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdnoreturn.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/xattr.h>
9 #include <fcntl.h>
10 #include <dirent.h>
11 #include <errno.h>
12 #include <linux/capability.h>
13 #include <sys/prctl.h>
14 #include <limits.h>
15 #include <stdint.h>
16 #include <syscall.h>
17 #include <byteswap.h>
19 #define ASSERT(expr) ((expr) ? (void) 0 : assert_failure(#expr))
21 extern char **environ;
23 // The WRAPPER_DIR macro is supplied at compile time so that it cannot
24 // be changed at runtime
25 static char *wrapper_dir = WRAPPER_DIR;
27 // Wrapper debug variable name
28 static char *wrapper_debug = "WRAPPER_DEBUG";
30 #define CAP_SETPCAP 8
32 #if __BYTE_ORDER == __BIG_ENDIAN
33 #define LE32_TO_H(x) bswap_32(x)
34 #else
35 #define LE32_TO_H(x) (x)
36 #endif
38 static noreturn void assert_failure(const char *assertion) {
39 fprintf(stderr, "Assertion `%s` in NixOS's wrapper.c failed.\n", assertion);
40 fflush(stderr);
41 abort();
44 int get_last_cap(unsigned *last_cap) {
45 FILE* file = fopen("/proc/sys/kernel/cap_last_cap", "r");
46 if (file == NULL) {
47 int saved_errno = errno;
48 fprintf(stderr, "failed to open /proc/sys/kernel/cap_last_cap: %s\n", strerror(errno));
49 return -saved_errno;
51 int res = fscanf(file, "%u", last_cap);
52 if (res == EOF) {
53 int saved_errno = errno;
54 fprintf(stderr, "could not read number from /proc/sys/kernel/cap_last_cap: %s\n", strerror(errno));
55 return -saved_errno;
57 fclose(file);
58 return 0;
61 // Given the path to this program, fetch its configured capability set
62 // (as set by `setcap ... /path/to/file`) and raise those capabilities
63 // into the Ambient set.
64 static int make_caps_ambient(const char *self_path) {
65 struct vfs_ns_cap_data data = {};
66 int r = getxattr(self_path, "security.capability", &data, sizeof(data));
68 if (r < 0) {
69 if (errno == ENODATA) {
70 // no capabilities set
71 return 0;
73 fprintf(stderr, "cannot get capabilities for %s: %s", self_path, strerror(errno));
74 return 1;
77 size_t size;
78 uint32_t version = LE32_TO_H(data.magic_etc) & VFS_CAP_REVISION_MASK;
79 switch (version) {
80 case VFS_CAP_REVISION_1:
81 size = VFS_CAP_U32_1;
82 break;
83 case VFS_CAP_REVISION_2:
84 case VFS_CAP_REVISION_3:
85 size = VFS_CAP_U32_3;
86 break;
87 default:
88 fprintf(stderr, "BUG! Unsupported capability version 0x%x on %s. Report to NixOS bugtracker\n", version, self_path);
89 return 1;
92 const struct __user_cap_header_struct header = {
93 .version = _LINUX_CAPABILITY_VERSION_3,
94 .pid = getpid(),
96 struct __user_cap_data_struct user_data[2] = {};
98 for (size_t i = 0; i < size; i++) {
99 // merge inheritable & permitted into one
100 user_data[i].permitted = user_data[i].inheritable =
101 LE32_TO_H(data.data[i].inheritable) | LE32_TO_H(data.data[i].permitted);
104 if (syscall(SYS_capset, &header, &user_data) < 0) {
105 fprintf(stderr, "failed to inherit capabilities: %s", strerror(errno));
106 return 1;
108 unsigned last_cap;
109 r = get_last_cap(&last_cap);
110 if (r < 0) {
111 return 1;
113 uint64_t set = user_data[0].permitted | (uint64_t)user_data[1].permitted << 32;
114 for (unsigned cap = 0; cap < last_cap; cap++) {
115 if (!(set & (1ULL << cap))) {
116 continue;
119 // Check for the cap_setpcap capability, we set this on the
120 // wrapper so it can elevate the capabilities to the Ambient
121 // set but we do not want to propagate it down into the
122 // wrapped program.
124 // TODO: what happens if that's the behavior you want
125 // though???? I'm preferring a strict vs. loose policy here.
126 if (cap == CAP_SETPCAP) {
127 if(getenv(wrapper_debug)) {
128 fprintf(stderr, "cap_setpcap in set, skipping it\n");
130 continue;
132 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long) cap, 0, 0)) {
133 fprintf(stderr, "cannot raise the capability %d into the ambient set: %s\n", cap, strerror(errno));
134 return 1;
136 if (getenv(wrapper_debug)) {
137 fprintf(stderr, "raised %d into the ambient capability set\n", cap);
141 return 0;
144 int readlink_malloc(const char *p, char **ret) {
145 size_t l = FILENAME_MAX+1;
146 int r;
148 for (;;) {
149 char *c = calloc(l, sizeof(char));
150 if (!c) {
151 return -ENOMEM;
154 ssize_t n = readlink(p, c, l-1);
155 if (n < 0) {
156 r = -errno;
157 free(c);
158 return r;
161 if ((size_t) n < l-1) {
162 c[n] = 0;
163 *ret = c;
164 return 0;
167 free(c);
168 l *= 2;
172 int main(int argc, char **argv) {
173 ASSERT(argc >= 1);
174 char *self_path = NULL;
175 int self_path_size = readlink_malloc("/proc/self/exe", &self_path);
176 if (self_path_size < 0) {
177 fprintf(stderr, "cannot readlink /proc/self/exe: %s", strerror(-self_path_size));
180 // Make sure that we are being executed from the right location,
181 // i.e., `safe_wrapper_dir'. This is to prevent someone from creating
182 // hard link `X' from some other location, along with a false
183 // `X.real' file, to allow arbitrary programs from being executed
184 // with elevated capabilities.
185 int len = strlen(wrapper_dir);
186 if (len > 0 && '/' == wrapper_dir[len - 1])
187 --len;
188 ASSERT(!strncmp(self_path, wrapper_dir, len));
189 ASSERT('/' == wrapper_dir[0]);
190 ASSERT('/' == self_path[len]);
192 // Make *really* *really* sure that we were executed as
193 // `self_path', and not, say, as some other setuid program. That
194 // is, our effective uid/gid should match the uid/gid of
195 // `self_path'.
196 struct stat st;
197 ASSERT(lstat(self_path, &st) != -1);
199 ASSERT(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid()));
200 ASSERT(!(st.st_mode & S_ISGID) || (st.st_gid == getegid()));
202 // And, of course, we shouldn't be writable.
203 ASSERT(!(st.st_mode & (S_IWGRP | S_IWOTH)));
205 // Read the path of the real (wrapped) program from <self>.real.
206 char real_fn[PATH_MAX + 10];
207 int real_fn_size = snprintf(real_fn, sizeof(real_fn), "%s.real", self_path);
208 ASSERT(real_fn_size < sizeof(real_fn));
210 int fd_self = open(real_fn, O_RDONLY);
211 ASSERT(fd_self != -1);
213 char source_prog[PATH_MAX];
214 len = read(fd_self, source_prog, PATH_MAX);
215 ASSERT(len != -1);
216 ASSERT(len < sizeof(source_prog));
217 ASSERT(len > 0);
218 source_prog[len] = 0;
220 close(fd_self);
222 // Read the capabilities set on the wrapper and raise them in to
223 // the ambient set so the program we're wrapping receives the
224 // capabilities too!
225 if (make_caps_ambient(self_path) != 0) {
226 free(self_path);
227 return 1;
229 free(self_path);
231 execve(source_prog, argv, environ);
233 fprintf(stderr, "%s: cannot run `%s': %s\n",
234 argv[0], source_prog, strerror(errno));
236 return 1;