1 // SPDX-License-Identifier: CDDL-1.0 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or https://opensource.org/licenses/CDDL-1.0. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 #include <stdio.h> 24 #include <unistd.h> 25 #include <string.h> 26 #include <limits.h> 27 #include <sys/types.h> 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <sys/wait.h> 31 #include <fcntl.h> 32 #include <errno.h> 33 #include <signal.h> 34 #include <sched.h> 35 36 #define EXECSHELL "/bin/sh" 37 #define UIDMAP "0 100000 65536" 38 39 static int 40 child_main(int argc, char *argv[], int sync_pipe) 41 { 42 char sync_buf; 43 char cmds[BUFSIZ] = { 0 }; 44 char sep[] = " "; 45 int i, len; 46 47 if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) { 48 perror("unshare"); 49 return (1); 50 } 51 52 /* tell parent we entered the new namespace */ 53 if (write(sync_pipe, "1", 1) != 1) { 54 perror("write"); 55 return (1); 56 } 57 58 /* wait for parent to setup the uid mapping */ 59 if (read(sync_pipe, &sync_buf, 1) != 1) { 60 (void) fprintf(stderr, "user namespace setup failed\n"); 61 return (1); 62 } 63 64 close(sync_pipe); 65 66 if (setuid(0) != 0) { 67 perror("setuid"); 68 return (1); 69 } 70 if (setgid(0) != 0) { 71 perror("setgid"); 72 return (1); 73 } 74 75 len = 0; 76 for (i = 1; i < argc; i++) { 77 (void) snprintf(cmds+len, sizeof (cmds)-len, 78 "%s%s", argv[i], sep); 79 len += strlen(argv[i]) + strlen(sep); 80 } 81 82 if (execl(EXECSHELL, "sh", "-c", cmds, (char *)NULL) != 0) { 83 perror("execl: " EXECSHELL); 84 return (1); 85 } 86 87 return (0); 88 } 89 90 static int 91 set_idmap(pid_t pid, const char *file) 92 { 93 int result = 0; 94 int mapfd; 95 char path[PATH_MAX]; 96 97 (void) snprintf(path, sizeof (path), "/proc/%d/%s", (int)pid, file); 98 99 mapfd = open(path, O_WRONLY); 100 if (mapfd < 0) { 101 perror("open"); 102 return (errno); 103 } 104 105 if (write(mapfd, UIDMAP, sizeof (UIDMAP)-1) != sizeof (UIDMAP)-1) { 106 perror("write"); 107 result = (errno); 108 } 109 110 close(mapfd); 111 112 return (result); 113 } 114 115 int 116 main(int argc, char *argv[]) 117 { 118 char sync_buf; 119 int result, wstatus; 120 int syncfd[2]; 121 pid_t child; 122 123 if (argc < 2 || strlen(argv[1]) == 0) { 124 (void) printf("\tUsage: %s <commands> ...\n", argv[0]); 125 return (1); 126 } 127 128 if (socketpair(AF_UNIX, SOCK_STREAM, 0, syncfd) != 0) { 129 perror("socketpair"); 130 return (1); 131 } 132 133 child = fork(); 134 if (child == (pid_t)-1) { 135 perror("fork"); 136 return (1); 137 } 138 139 if (child == 0) { 140 close(syncfd[0]); 141 return (child_main(argc, argv, syncfd[1])); 142 } 143 144 close(syncfd[1]); 145 146 result = 0; 147 /* wait for the child to have unshared its namespaces */ 148 if (read(syncfd[0], &sync_buf, 1) != 1) { 149 perror("read"); 150 kill(child, SIGKILL); 151 result = 1; 152 goto reap; 153 } 154 155 /* write uid mapping */ 156 if (set_idmap(child, "uid_map") != 0 || 157 set_idmap(child, "gid_map") != 0) { 158 result = 1; 159 kill(child, SIGKILL); 160 goto reap; 161 } 162 163 /* tell the child to proceed */ 164 if (write(syncfd[0], "1", 1) != 1) { 165 perror("write"); 166 kill(child, SIGKILL); 167 result = 1; 168 goto reap; 169 } 170 close(syncfd[0]); 171 172 reap: 173 while (waitpid(child, &wstatus, 0) != child) 174 kill(child, SIGKILL); 175 if (result == 0) 176 result = WEXITSTATUS(wstatus); 177 178 return (result); 179 } 180