dserverdbg.c
1 #define _GNU_SOURCE 2 #include <darlingserver/rpc.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <stdio.h> 6 #include <string.h> 7 #include <pwd.h> 8 #include <fcntl.h> 9 #include <sched.h> 10 #include <errno.h> 11 #include <signal.h> 12 #include <sys/un.h> 13 #include <sys/socket.h> 14 15 typedef uint32_t mach_port_type_t; 16 typedef uint32_t mach_port_right_t; 17 18 #define MACH_PORT_RIGHT_SEND ((mach_port_right_t) 0) 19 #define MACH_PORT_RIGHT_RECEIVE ((mach_port_right_t) 1) 20 #define MACH_PORT_RIGHT_SEND_ONCE ((mach_port_right_t) 2) 21 #define MACH_PORT_RIGHT_PORT_SET ((mach_port_right_t) 3) 22 #define MACH_PORT_RIGHT_DEAD_NAME ((mach_port_right_t) 4) 23 #define MACH_PORT_RIGHT_LABELH ((mach_port_right_t) 5) /* obsolete right */ 24 #define MACH_PORT_RIGHT_NUMBER ((mach_port_right_t) 6) /* right not implemented */ 25 26 #define MACH_PORT_TYPE(right) \ 27 ((mach_port_type_t)(((mach_port_type_t) 1) \ 28 << ((right) + ((mach_port_right_t) 16)))) 29 #define MACH_PORT_TYPE_NONE ((mach_port_type_t) 0L) 30 #define MACH_PORT_TYPE_SEND MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND) 31 #define MACH_PORT_TYPE_RECEIVE MACH_PORT_TYPE(MACH_PORT_RIGHT_RECEIVE) 32 #define MACH_PORT_TYPE_SEND_ONCE MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND_ONCE) 33 #define MACH_PORT_TYPE_PORT_SET MACH_PORT_TYPE(MACH_PORT_RIGHT_PORT_SET) 34 #define MACH_PORT_TYPE_DEAD_NAME MACH_PORT_TYPE(MACH_PORT_RIGHT_DEAD_NAME) 35 #define MACH_PORT_TYPE_LABELH MACH_PORT_TYPE(MACH_PORT_RIGHT_LABELH) /* obsolete */ 36 37 // borrowed from `src/startup/darling.c` 38 // --- 39 // Between Linux 4.9 and 4.11, a strange bug has been introduced 40 // which prevents connecting to Unix sockets if the socket was 41 // created in a different mount namespace or under overlayfs 42 // (dunno which one is really responsible for this). 43 #define USE_LINUX_4_11_HACK 1 44 45 typedef enum dserverdbg_command { 46 dserverdbg_command_ps, 47 dserverdbg_command_lsport, 48 dserverdbg_command_lspset, 49 dserverdbg_command_lsmsg, 50 } dserverdbg_command_t; 51 52 struct sockaddr_un __dserver_socket_address_data = {0}; 53 int __dserver_main_thread_socket_fd = -1; 54 55 static char* default_prefix_path(uid_t original_uid) { 56 struct passwd* info = getpwuid(original_uid); 57 char* result = NULL; 58 59 if (asprintf(&result, "%s/.darling", info->pw_dir) < 0) { 60 return NULL; 61 } 62 63 return result; 64 }; 65 66 static char* get_prefix_path(uid_t original_uid) { 67 char* env = getenv("DPREFIX"); 68 69 if (env) { 70 return strdup(env); 71 } 72 73 return default_prefix_path(original_uid); 74 }; 75 76 // borrowed from `src/startup/darling.c` 77 static void joinNamespace(pid_t pid, int type, const char* typeName) 78 { 79 int fdNS; 80 char pathNS[4096]; 81 82 snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/%s", pid, typeName); 83 84 fdNS = open(pathNS, O_RDONLY); 85 86 if (fdNS < 0) 87 { 88 fprintf(stderr, "Cannot open %s namespace file: %s\n", typeName, strerror(errno)); 89 exit(1); 90 } 91 92 // Calling setns() with a PID namespace doesn't move our process into it, 93 // but our child process will be spawned inside the namespace 94 if (setns(fdNS, type) != 0) 95 { 96 fprintf(stderr, "Cannot join %s namespace: %s\n", typeName, strerror(errno)); 97 exit(1); 98 } 99 close(fdNS); 100 } 101 102 // borrowed from `src/startup/darling.c`, with the UID/GID check removed 103 static pid_t getInitProcess(const char* prefix) 104 { 105 const char pidFile[] = "/.init.pid"; 106 char* pidPath; 107 pid_t pid; 108 int pid_i; 109 FILE *fp; 110 char procBuf[100]; 111 char *exeBuf; 112 113 pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile)); 114 strcpy(pidPath, prefix); 115 strcat(pidPath, pidFile); 116 117 fp = fopen(pidPath, "r"); 118 if (fp == NULL) 119 return 0; 120 121 if (fscanf(fp, "%d", &pid_i) != 1) 122 { 123 fclose(fp); 124 unlink(pidPath); 125 return 0; 126 } 127 fclose(fp); 128 pid = (pid_t) pid_i; 129 130 // Does the process exist? 131 if (kill(pid, 0) == -1) 132 { 133 unlink(pidPath); 134 return 0; 135 } 136 137 // Is it actually an init process? 138 snprintf(procBuf, sizeof(procBuf), "/proc/%d/comm", pid); 139 fp = fopen(procBuf, "r"); 140 if (fp == NULL) 141 { 142 unlink(pidPath); 143 return 0; 144 } 145 146 if (fscanf(fp, "%ms", &exeBuf) != 1) 147 { 148 fclose(fp); 149 unlink(pidPath); 150 return 0; 151 } 152 fclose(fp); 153 154 if (strcmp(exeBuf, "darlingserver") != 0) 155 { 156 unlink(pidPath); 157 return 0; 158 } 159 free(exeBuf); 160 161 return pid; 162 } 163 164 static int setup_socket(void) { 165 int fd = -1; 166 167 fd = socket(AF_UNIX, SOCK_DGRAM, 0); 168 if (fd < 0) { 169 goto err_out; 170 } 171 172 int fd_flags = fcntl(fd, F_GETFD); 173 if (fd_flags < 0) { 174 goto err_out; 175 } 176 if (fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC) < 0) { 177 goto err_out; 178 } 179 180 sa_family_t family = AF_UNIX; 181 if (bind(fd, (const struct sockaddr*)&family, sizeof(family)) < 0) { 182 goto err_out; 183 } 184 185 out: 186 return fd; 187 188 err_out: 189 if (fd >= 0) { 190 close(fd); 191 } 192 193 return -1; 194 }; 195 196 // borrowed from `src/startup/darling.c` 197 static void missingSetuidRoot(void) 198 { 199 char path[4096]; 200 int len; 201 202 len = readlink("/proc/self/exe", path, sizeof(path)-1); 203 if (len < 0) 204 strcpy(path, "darling"); 205 else 206 path[len] = '\0'; 207 208 fprintf(stderr, "Sorry, the `%s' binary is not setuid root, which is mandatory.\n", path); 209 fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n"); 210 } 211 212 int main(int argc, char** argv) { 213 char* prefix_path = NULL; 214 dserverdbg_command_t command = dserverdbg_command_ps; 215 pid_t command_pid = 0; 216 uint32_t command_port = 0; 217 int output_fd = -1; 218 int status = 0; 219 uint64_t count = 0; 220 uint64_t elmsize = 0; 221 char* data = 0; 222 uid_t original_uid = -1; 223 gid_t original_gid = -1; 224 #if USE_LINUX_4_11_HACK 225 pid_t pidInit = 0; 226 #endif 227 228 if (geteuid() != 0) { 229 missingSetuidRoot(); 230 return 1; 231 } 232 233 original_uid = getuid(); 234 original_gid = getgid(); 235 236 setuid(0); 237 setgid(0); 238 239 prefix_path = get_prefix_path(original_uid); 240 if (!prefix_path) { 241 fprintf(stderr, "Failed to determine prefix path\n"); 242 return 1; 243 } 244 245 __dserver_socket_address_data.sun_family = AF_UNIX; 246 snprintf(__dserver_socket_address_data.sun_path, sizeof(__dserver_socket_address_data.sun_path), "%s/.darlingserver.sock", prefix_path); 247 248 #if USE_LINUX_4_11_HACK 249 pidInit = getInitProcess(prefix_path); 250 joinNamespace(pidInit, CLONE_NEWNS, "mnt"); 251 #endif 252 253 __dserver_main_thread_socket_fd = setup_socket(); 254 255 if (__dserver_main_thread_socket_fd < 0) { 256 fprintf(stderr, "Failed to set up darlingserver client socket\n"); 257 return 1; 258 } 259 260 if (argc > 1) { 261 if (strcmp(argv[1], "ps") == 0 || strcmp(argv[1], "lsproc") == 0) { 262 command = dserverdbg_command_ps; 263 } else if (strcmp(argv[1], "lsport") == 0) { 264 command = dserverdbg_command_lsport; 265 } else if (strcmp(argv[1], "lspset") == 0) { 266 command = dserverdbg_command_lspset; 267 } else if (strcmp(argv[1], "lsmsg") == 0) { 268 command = dserverdbg_command_lsmsg; 269 } else { 270 fprintf(stderr, "Unknown subcommand: %s\n", argv[1]); 271 return 1; 272 } 273 } 274 275 switch (command) { 276 case dserverdbg_command_ps: 277 if (argc > 2) { 278 fprintf(stderr, "Expected 1 argument (subcommand); got %d arguments\n", argc); 279 return 1; 280 } 281 break; 282 case dserverdbg_command_lsport: 283 if (argc != 3) { 284 fprintf(stderr, "Expected 2 arguments (subcommand and PID); got %d arguments\n", argc); 285 return 1; 286 } 287 command_pid = atoi(argv[2]); 288 break; 289 case dserverdbg_command_lspset: 290 case dserverdbg_command_lsmsg: 291 if (argc != 4) { 292 fprintf(stderr, "Expected 3 arguments (subcommand, PID, and port name); got %d arguments\n", argc); 293 return 1; 294 } 295 command_pid = atoi(argv[2]); 296 command_port = atoi(argv[3]); 297 break; 298 } 299 300 switch (command) { 301 case dserverdbg_command_ps: 302 status = dserver_rpc_debug_list_processes(&count, &output_fd); 303 elmsize = sizeof(dserver_debug_process_t); 304 break; 305 case dserverdbg_command_lsport: 306 status = dserver_rpc_debug_list_ports(command_pid, &count, &output_fd); 307 elmsize = sizeof(dserver_debug_port_t); 308 break; 309 case dserverdbg_command_lspset: 310 status = dserver_rpc_debug_list_members(command_pid, command_port, &count, &output_fd); 311 elmsize = sizeof(dserver_debug_port_t); 312 break; 313 case dserverdbg_command_lsmsg: 314 status = dserver_rpc_debug_list_messages(command_pid, command_port, &count, &output_fd); 315 elmsize = sizeof(dserver_debug_message_t); 316 break; 317 } 318 319 if (status != 0) { 320 fprintf(stderr, "Subcommand failed: server replied with error: %d (%s)\n", status, strerror(status)); 321 return 1; 322 } 323 324 for (uint64_t i = 0; i < count; ++i) { 325 char buffer[elmsize]; 326 327 if (read(output_fd, buffer, elmsize) != elmsize) { 328 status = errno; 329 fprintf(stderr, "Failed to read from output pipe: %d (%s)\n", status, strerror(status)); 330 return 1; 331 } 332 333 switch (command) { 334 case dserverdbg_command_ps: { 335 dserver_debug_process_t* data = (void*)buffer; 336 printf("pid %u - %lu ports\n", data->pid, data->port_count); 337 } break; 338 339 case dserverdbg_command_lsport: 340 case dserverdbg_command_lspset: { 341 dserver_debug_port_t* data = (void*)buffer; 342 const char* right_name = "<unknown>"; 343 344 if (data->rights == MACH_PORT_TYPE_SEND) { 345 right_name = "send"; 346 } else if (data->rights == MACH_PORT_TYPE_RECEIVE) { 347 right_name = "receive"; 348 } else if (data->rights == MACH_PORT_TYPE_SEND_ONCE) { 349 right_name = "send-once"; 350 } else if (data->rights == MACH_PORT_TYPE_PORT_SET) { 351 right_name = "port set"; 352 } else if (data->rights == MACH_PORT_TYPE_DEAD_NAME) { 353 right_name = "dead name"; 354 } else if (data->rights == MACH_PORT_TYPE_LABELH) { 355 right_name = "labelh"; 356 } 357 358 printf("port %d (%s), %lu refs - %lu messages\n", data->port_name, right_name, data->refs, data->messages); 359 } break; 360 361 case dserverdbg_command_lsmsg: { 362 dserver_debug_message_t* data = (void*)buffer; 363 364 printf("message %lu (from %u); %lu bytes\n", i, data->sender, data->size); 365 } break; 366 } 367 } 368 369 if (output_fd >= 0) { 370 close(output_fd); 371 } 372 373 if (prefix_path) { 374 free(prefix_path); 375 } 376 377 return 0; 378 };