The Pedigree Project 0.1
credentials.c
1#define _GNU_SOURCE
2#include <grp.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/fsuid.h>
8#include <sys/mman.h>
9#include <sys/prctl.h>
10
11static int dumpable(void) {
12 return prctl(PR_GET_DUMPABLE, 0UL, 0UL, 0UL, 0UL);
13}
14static int self(void) {
15 int failed = 0;
16 unsigned char source = 0x31, destination = 0;
17 CHECK(!pm_dumpable(0) && dumpable() == 0);
18 CHECK(pm_copy(getpid(), &destination, &source, 1, 0) == 1 && destination == 0x31);
19 destination = 0x42;
20 CHECK(pm_copy(getpid(), &destination, &source, 1, 1) == 1 && source == 0x42);
21 errno = 0;
22 CHECK(prctl(PR_SET_DUMPABLE, 2UL, 0UL, 0UL, 0UL) == -1 && errno == EINVAL);
23 CHECK(dumpable() == 0 && !pm_dumpable(1) && dumpable() == 1);
24out:
25 return failed;
26}
27static int policy_target(int command, int report, void* ignored) {
28 (void)ignored;
29 int failed = 0;
30 volatile unsigned char byte;
31 uintptr_t address = (uintptr_t)&byte;
32 for (int step = 0; step < 8; ++step) {
33 CHECK(!setresuid(0, 0, 0) && !setresgid(0, 0, 0));
34 switch (step) {
35 case 2:
36 CHECK(!setresuid(70001, -1, -1));
37 break;
38 case 3:
39 CHECK(!setresuid(-1, 70001, -1));
40 break;
41 case 4:
42 CHECK(!setresuid(-1, -1, 70001));
43 break;
44 case 5:
45 CHECK(!setresgid(70002, -1, -1));
46 break;
47 case 6:
48 CHECK(!setresgid(-1, 70002, -1));
49 break;
50 case 7:
51 CHECK(!setresgid(-1, -1, 70002));
52 break;
53 }
54 CHECK(!pm_dumpable(step != 1));
55 byte = 0x4a;
56 CHECK(!pm_write(report, &address, sizeof(address)) && !pm_receive(command, 'n'));
57 CHECK(byte == (step == 0 ? 0x5b : 0x4a));
58 }
59out:
60 return failed;
61}
62static int policy(void) {
63 int failed = 0;
64 struct pm_peer peer = PM_PEER_INITIALIZER;
65 uintptr_t address;
66 unsigned char byte;
67 CHECK(getuid() == 0 && geteuid() == 0 && getgid() == 0 && getegid() == 0);
68 CHECK(!pm_spawn(&peer, policy_target, NULL));
69 for (int step = 0; step < 8; ++step) {
70 CHECK(!pm_read(peer.report, &address, sizeof(address)));
71 byte = 0x6c;
72 if (!step) {
73 CHECK(pm_copy(peer.pid, &byte, (void*)address, 1, 0) == 1 && byte == 0x4a);
74 CHECK(setfsuid(70001) == 0 && setfsgid(70002) == 0);
75 CHECK(!seteuid(70003) && getuid() == 0 && geteuid() == 70003);
76 CHECK(pm_copy(peer.pid, &byte, (void*)address, 1, 0) == 1 && byte == 0x4a);
77 CHECK(!seteuid(0) && setfsgid(0) == 70002);
78 byte = 0x5b;
79 CHECK(pm_copy(peer.pid, &byte, (void*)address, 1, 1) == 1);
80 } else {
81 errno = 0;
82 CHECK(pm_copy(peer.pid, &byte, (void*)address, 1, 0) == -1 && errno == EPERM);
83 CHECK(byte == 0x6c);
84 errno = 0;
85 CHECK(pm_copy(peer.pid, &byte, (void*)address, 1, 1) == -1 && errno == EPERM);
86 }
87 CHECK(!pm_send(peer.command, 'n'));
88 }
89 CHECK(!pm_join(&peer));
90out:
91 seteuid(0);
92 setfsuid(0);
93 setfsgid(0);
94 pm_cleanup(&peer);
95 return failed;
96}
97static int ids(uid_t r, uid_t e, uid_t s, gid_t gr, gid_t ge, gid_t gs) {
98 uid_t actual_r, actual_e, actual_s;
99 gid_t actual_gr, actual_ge, actual_gs;
100 return getresuid(&actual_r, &actual_e, &actual_s) ||
101 getresgid(&actual_gr, &actual_ge, &actual_gs) || actual_r != r || actual_e != e ||
102 actual_s != s || actual_gr != gr || actual_ge != ge || actual_gs != gs;
103}
104static int transitions(void) {
105 int failed = 0;
106 pid_t child = -1;
107 CHECK(!setresgid(70001, 70002, 70003) && !setresuid(71001, 71002, 71003));
108 CHECK(!ids(71001, 71002, 71003, 70001, 70002, 70003) && dumpable() == 0);
109 CHECK(setfsuid(-1) == 71002 && setfsgid(-1) == 70002);
110 CHECK(!pm_dumpable(1) && setfsuid(71003) == 71002 && dumpable() == 0);
111 errno = 0;
112 CHECK(setfsuid(79999) == 71003 && errno == 0 && setfsuid(-1) == 71003);
113 CHECK(!setresuid(-1, -1, -1) && setfsuid(-1) == 71003);
114 CHECK(!setresuid(-1, 71002, -1) && setfsuid(-1) == 71002);
115 CHECK(setfsgid(70003) == 70002);
116 errno = 0;
117 CHECK(setfsgid(79999) == 70003 && errno == 0 && setfsgid(-1) == 70003);
118 CHECK(!setresgid(-1, 70002, -1) && setfsgid(-1) == 70002);
119 errno = 0;
120 CHECK(setresuid(71002, 79999, -1) == -1 && errno == EPERM);
121 CHECK(!ids(71001, 71002, 71003, 70001, 70002, 70003));
122 errno = 0;
123 CHECK(setresgid(70002, 79999, -1) == -1 && errno == EPERM);
124 CHECK(!ids(71001, 71002, 71003, 70001, 70002, 70003));
125 CHECK(!pm_dumpable(1));
126 execl("/applications/process-memory-missing", "missing", (char*)NULL);
127 CHECK(errno == ENOENT && dumpable() == 1 && !ids(71001, 71002, 71003, 70001, 70002, 70003));
128 child = fork();
129 CHECK(child >= 0);
130 if (!child) {
131 if (ids(71001, 71002, 71003, 70001, 70002, 70003) || dumpable() != 1)
132 _exit(10);
133 execl(PM_APP, PM_APP, "fs-exec", "71001", "71002", "70001", "70002", "0", (char*)NULL);
134 _exit(11);
135 }
136 int status = pm_reap(child, 10000);
137 child = -1;
138 CHECK(status == 0);
139 CHECK(!setuid(71001) && !setgid(70001));
140 CHECK(!ids(71001, 71001, 71003, 70001, 70001, 70003));
141 errno = 0;
142 CHECK(setuid(79999) == -1 && errno == EPERM);
143 errno = 0;
144 CHECK(setgid(79999) == -1 && errno == EPERM);
145 CHECK(!ids(71001, 71001, 71003, 70001, 70001, 70003));
146out:
147 if (child > 0)
148 pm_reap(child, 100);
149 return failed;
150}
151static int groups(void) {
152 int failed = 0;
153 gid_t chosen[] = {70003, 70001, 70003}, actual[3], invalid = (gid_t)-1;
154 void* bad = mmap(NULL, pm_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
155 CHECK(bad != MAP_FAILED && !setgroups(3, chosen));
156 CHECK(getgroups(0, NULL) == 3 && getgroups(3, actual) == 3);
157 CHECK(actual[0] == 70001 && actual[1] == 70003 && actual[2] == 70003);
158 errno = 0;
159 CHECK(setgroups(33, NULL) == -1 && errno == EINVAL);
160 errno = 0;
161 CHECK(setgroups(1, bad) == -1 && errno == EFAULT);
162 errno = 0;
163 CHECK(setgroups(1, &invalid) == -1 && errno == EINVAL);
164 CHECK(getgroups(3, actual) == 3 && actual[0] == 70001 && actual[2] == 70003);
165 CHECK(!setuid(71001));
166 errno = 0;
167 CHECK(setgroups(0, NULL) == -1 && errno == EPERM);
168 CHECK(getgroups(0, NULL) == 3);
169out:
170 if (bad != MAP_FAILED)
171 munmap(bad, pm_page);
172 return failed;
173}
174int pm_credentials(void) {
175 return self() || policy() || pm_isolate(transitions) || pm_isolate(groups);
176}