xref: /aosp_15_r20/external/libcap/tests/libcap_launch_test.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
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