1 #include <errno.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/capability.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <unistd.h>
9
10 /*
11 * tests for cap_launch.
12 */
13
14 #define MORE_THAN_ENOUGH 20
15 #define NO_MORE 1
16
17 struct test_case_s {
18 int pass_on;
19 const char *chroot;
20 uid_t uid;
21 gid_t gid;
22 int ngroups;
23 const gid_t groups[MORE_THAN_ENOUGH];
24 const char *args[MORE_THAN_ENOUGH];
25 const char **envp;
26 const char *iab;
27 cap_mode_t mode;
28 int launch_abort;
29 int result;
30 int (*callback_fn)(void *detail);
31 };
32
33 #ifdef WITH_PTHREADS
34 #include <pthread.h>
35 #else /* WITH_PTHREADS */
36 #endif /* WITH_PTHREADS */
37
38 /*
39 * clean_out drops all process capabilities.
40 */
clean_out(void * data)41 static int clean_out(void *data) {
42 cap_t empty;
43 empty = cap_init();
44 if (cap_set_proc(empty) != 0) {
45 _exit(1);
46 }
47 cap_free(empty);
48 return 0;
49 }
50
main(int argc,char ** argv)51 int main(int argc, char **argv) {
52 static struct test_case_s vs[] = {
53 {
54 .args = { "../progs/tcapsh-static", "--", "-c", "echo hello" },
55 .result = 0
56 },
57 {
58 .args = { "../progs/tcapsh-static", "--", "-c", "echo hello" },
59 .callback_fn = &clean_out,
60 .result = 0
61 },
62 {
63 .callback_fn = &clean_out,
64 .result = 0
65 },
66 {
67 .args = { "../progs/tcapsh-static", "--is-uid=123" },
68 .result = 256
69 },
70 {
71 .args = { "/", "won't", "work" },
72 .launch_abort = 1,
73 },
74 {
75 .args = { "../progs/tcapsh-static", "--is-uid=123" },
76 .uid = 123,
77 .result = 0,
78 },
79 {
80 .args = { "../progs/tcapsh-static", "--is-uid=123" },
81 .callback_fn = &clean_out,
82 .uid = 123,
83 .launch_abort = 1,
84 },
85 {
86 .args = { "../progs/tcapsh-static", "--is-gid=123" },
87 .result = 0,
88 .gid = 123,
89 .ngroups = 1,
90 .groups = { 456 },
91 .iab = "",
92 },
93 {
94 .args = { "../progs/tcapsh-static", "--dropped=cap_chown",
95 "--has-i=cap_chown" },
96 .result = 0,
97 .iab = "!%cap_chown"
98 },
99 {
100 .args = { "../progs/tcapsh-static", "--dropped=cap_chown",
101 "--has-i=cap_chown", "--is-uid=234",
102 "--has-a=cap_chown", "--has-p=cap_chown" },
103 .uid = 234,
104 .result = 0,
105 .iab = "!^cap_chown"
106 },
107 {
108 .args = { "../progs/tcapsh-static", "--inmode=NOPRIV",
109 "--has-no-new-privs" },
110 .result = 0,
111 .mode = CAP_MODE_NOPRIV
112 },
113 {
114 .args = { "/noop" },
115 .result = 0,
116 .chroot = ".",
117 },
118 {
119 .pass_on = NO_MORE
120 },
121 };
122
123 if (errno != 0) {
124 perror("unexpected initial value for errno");
125 exit(1);
126 }
127
128 cap_t orig = cap_get_proc();
129 if (orig == NULL) {
130 perror("failed to get process capabilities");
131 exit(1);
132 }
133
134 int success = 1, i;
135 for (i=0; vs[i].pass_on != NO_MORE; i++) {
136 cap_launch_t attr = NULL;
137 const struct test_case_s *v = &vs[i];
138 if (cap_launch(attr, NULL) != -1) {
139 perror("NULL launch didn't fail");
140 exit(1);
141 }
142 printf("[%d] test should %s\n", i,
143 v->result || v->launch_abort ? "generate error" : "work");
144 if (v->args[0] != NULL) {
145 attr = cap_new_launcher(v->args[0], v->args, v->envp);
146 if (attr == NULL) {
147 perror("failed to obtain launcher");
148 exit(1);
149 }
150 if (v->callback_fn != NULL) {
151 cap_launcher_callback(attr, v->callback_fn);
152 }
153 } else {
154 attr = cap_func_launcher(v->callback_fn);
155 }
156 if (v->chroot) {
157 cap_launcher_set_chroot(attr, v->chroot);
158 }
159 if (v->uid) {
160 cap_launcher_setuid(attr, v->uid);
161 }
162 if (v->gid) {
163 cap_launcher_setgroups(attr, v->gid, v->ngroups, v->groups);
164 }
165 if (v->iab) {
166 cap_iab_t iab = cap_iab_from_text(v->iab);
167 if (iab == NULL) {
168 fprintf(stderr, "[%d] failed to decode iab [%s]", i, v->iab);
169 perror(":");
170 success = 0;
171 continue;
172 }
173 cap_iab_t old = cap_launcher_set_iab(attr, iab);
174 if (cap_free(old)) {
175 fprintf(stderr, "[%d] failed to decode iab [%s]", i, v->iab);
176 perror(":");
177 success = 0;
178 continue;
179 }
180 }
181 if (v->mode) {
182 cap_launcher_set_mode(attr, v->mode);
183 }
184
185 pid_t child = cap_launch(attr, NULL);
186
187 if (child <= 0) {
188 fprintf(stderr, "[%d] failed to launch: ", i);
189 perror("");
190 if (!v->launch_abort) {
191 success = 0;
192 }
193 continue;
194 }
195 if (cap_free(attr)) {
196 fprintf(stderr, "[%d] failed to free launcher: ", i);
197 perror("");
198 success = 0;
199 }
200 int result;
201 int ret = waitpid(child, &result, 0);
202 if (ret != child) {
203 fprintf(stderr, "[%d] failed to wait: ", i);
204 perror("");
205 success = 0;
206 continue;
207 }
208 if (result != v->result) {
209 fprintf(stderr, "[%d] bad result: got=%d want=%d: ", i, result,
210 v->result);
211 perror("");
212 success = 0;
213 continue;
214 }
215 }
216
217 cap_t final = cap_get_proc();
218 if (final == NULL) {
219 perror("unable to get final capabilities");
220 exit(1);
221 }
222 if (cap_compare(orig, final)) {
223 char *was = cap_to_text(orig, NULL);
224 char *is = cap_to_text(final, NULL);
225 printf("cap_launch_test: orig:'%s' != final:'%s'\n", was, is);
226 cap_free(is);
227 cap_free(was);
228 success = 0;
229 }
230 cap_free(final);
231 cap_free(orig);
232
233 if (!success) {
234 printf("cap_launch_test: FAILED\n");
235 exit(1);
236 }
237 printf("cap_launch_test: PASSED\n");
238 exit(0);
239 }
240