this repo has no description
at fixPythonPipStalling 1211 lines 25 kB view raw
1/* 2This file is part of Darling. 3 4Copyright (C) 2016-2023 Lubos Dolezel 5 6Darling is free software: you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation, either version 3 of the License, or 9(at your option) any later version. 10 11Darling is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with Darling. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include <stdio.h> 21#include <sys/types.h> 22#include <unistd.h> 23#include <stdlib.h> 24#include <string.h> 25#include <alloca.h> 26#include <errno.h> 27#include <fcntl.h> 28#include <sys/stat.h> 29#include <signal.h> 30#include <stdbool.h> 31#include <sched.h> 32#include <sys/poll.h> 33#include <sys/socket.h> 34#include <sys/un.h> 35#include <getopt.h> 36#include <termios.h> 37#include <pty.h> 38#include <pwd.h> 39#include "../shellspawn/shellspawn.h" 40#include "darling.h" 41#include "darling-config.h" 42 43// Between Linux 4.9 and 4.11, a strange bug has been introduced 44// which prevents connecting to Unix sockets if the socket was 45// created in a different mount namespace or under overlayfs 46// (dunno which one is really responsible for this). 47#define USE_LINUX_4_11_HACK 1 48 49char *prefix; 50uid_t g_originalUid, g_originalGid; 51bool g_fixPermissions = false; 52char g_workingDirectory[4096]; 53 54int main(int argc, char ** argv) 55{ 56 pid_t pidInit; 57 58 if (argc <= 1) 59 { 60 showHelp(argv[0]); 61 return 1; 62 } 63 64 if (geteuid() != 0) 65 { 66 missingSetuidRoot(); 67 return 1; 68 } 69 70 g_originalUid = getuid(); 71 g_originalGid = getgid(); 72 73 setuid(0); 74 setgid(0); 75 76 prefix = getenv("DPREFIX"); 77 if (!prefix) 78 prefix = defaultPrefixPath(); 79 if (!prefix) 80 return 1; 81 if (strlen(prefix) > 255) 82 { 83 fprintf(stderr, "Prefix path too long\n"); 84 return 1; 85 } 86 unsetenv("DPREFIX"); 87 getcwd(g_workingDirectory, sizeof(g_workingDirectory)); 88 89 if (!checkPrefixDir()) 90 { 91 setupPrefix(); 92 g_fixPermissions = true; 93 } 94 checkPrefixOwner(); 95 96 int c; 97 while (1) 98 { 99 static struct option long_options[] = 100 { 101 {"help", no_argument, 0, 0}, 102 {"version", no_argument, 0, 0}, 103 {0, 0, 0, 0} 104 }; 105 int option_index = 0; 106 107 c = getopt_long(argc, argv, "+", long_options, &option_index); 108 109 if (c == -1) 110 { 111 break; 112 } 113 114 switch (c) 115 { 116 case 0: 117 if (strcmp(long_options[option_index].name, "help") == 0) 118 { 119 showHelp(argv[0]); 120 exit(EXIT_SUCCESS); 121 } 122 else if (strcmp(long_options[option_index].name, "version") == 0) 123 { 124 showVersion(argv[0]); 125 exit(EXIT_SUCCESS); 126 } 127 break; 128 case '?': 129 break; 130 default: 131 abort(); 132 } 133 } 134 135 pidInit = getInitProcess(); 136 137 if (strcmp(argv[1], "shutdown") == 0) 138 { 139 if (pidInit == 0) 140 { 141 fprintf(stderr, "Darling container is not running\n"); 142 return 1; 143 } 144 145 // TODO: when we have a working launchd, 146 // this is where we ask it to shut down nicely 147 148 char path_buf[128]; 149 FILE* file; 150 pid_t launchd_pid; 151 snprintf(path_buf, sizeof(path_buf), "/proc/%d/task/%d/children", pidInit, pidInit); 152 file = fopen(path_buf, "r"); 153 if (!file || fscanf(file, "%d", &launchd_pid) != 1) { 154 fprintf(stderr, "Failed to shutdown Darling container\n"); 155 if (file) { 156 fclose(file); 157 } 158 return 1; 159 } 160 fclose(file); 161 162 kill(launchd_pid, SIGKILL); 163 kill(pidInit, SIGKILL); 164 return 0; 165 } 166 167 // If prefix's init is not running, start it up 168 if (pidInit == 0) 169 { 170 char socketPath[4096]; 171 172 snprintf(socketPath, sizeof(socketPath), "%s" SHELLSPAWN_SOCKPATH, prefix); 173 174 unlink(socketPath); 175 176 setupWorkdir(); 177 pidInit = spawnInitProcess(); 178 putInitPid(pidInit); 179 180 // Wait until shellspawn starts 181 for (int i = 0; i < 15; i++) 182 { 183 if (access(socketPath, F_OK) == 0) 184 break; 185 sleep(1); 186 } 187 } 188 189#if USE_LINUX_4_11_HACK 190 joinNamespace(pidInit, CLONE_NEWNS, "mnt"); 191#endif 192 193 seteuid(g_originalUid); 194 195 if (strcmp(argv[1], "shell") == 0) 196 { 197 // Spawn the shell 198 if (argc > 2) 199 spawnShell((const char**) &argv[2]); 200 else 201 spawnShell(NULL); 202 } 203 else 204 { 205 bool doExec = strcmp(argv[1], "exec") == 0; 206 int argvIndex = doExec ? 2 : 1; 207 208 if (doExec && argc <= 2) 209 { 210 printf("'exec' subcommand requires a binary to execute.\n"); 211 return 1; 212 } 213 214 char *fullPath; 215 char *path = realpath(argv[argvIndex], NULL); 216 217 if (path == NULL) 218 { 219 printf("'%s' is not a supported command or a file.\n", argv[argvIndex]); 220 return 1; 221 } 222 223 fullPath = malloc(strlen(SYSTEM_ROOT) + strlen(path) + 1); 224 strcpy(fullPath, SYSTEM_ROOT); 225 strcat(fullPath, path); 226 free(path); 227 228 argv[argvIndex] = fullPath; 229 230 if (doExec) 231 spawnBinary(argv[argvIndex], (const char**) &argv[argvIndex]); 232 else 233 spawnShell((const char**) &argv[argvIndex]); 234 } 235 236 return 0; 237} 238 239void joinNamespace(pid_t pid, int type, const char* typeName) 240{ 241 int fdNS; 242 char pathNS[4096]; 243 244 snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/%s", pid, typeName); 245 246 fdNS = open(pathNS, O_RDONLY); 247 248 if (fdNS < 0) 249 { 250 fprintf(stderr, "Cannot open %s namespace file: %s\n", typeName, strerror(errno)); 251 exit(1); 252 } 253 254 // Calling setns() with a PID namespace doesn't move our process into it, 255 // but our child process will be spawned inside the namespace 256 if (setns(fdNS, type) != 0) 257 { 258 fprintf(stderr, "Cannot join %s namespace: %s\n", typeName, strerror(errno)); 259 exit(1); 260 } 261 close(fdNS); 262} 263 264static void pushShellspawnCommandData(int sockfd, shellspawn_cmd_type_t type, const void* data, size_t data_length) 265{ 266 struct shellspawn_cmd* cmd; 267 size_t length; 268 269 length = sizeof(*cmd) + data_length; 270 271 cmd = (struct shellspawn_cmd*) malloc(length); 272 cmd->cmd = type; 273 cmd->data_length = data_length; 274 275 if (data != NULL) 276 memcpy(cmd->data, data, data_length); 277 278 if (write(sockfd, cmd, length) != length) 279 { 280 fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno)); 281 exit(EXIT_FAILURE); 282 } 283 284 free(cmd); 285} 286 287static void pushShellspawnCommand(int sockfd, shellspawn_cmd_type_t type, const char* value) 288{ 289 if (!value) 290 pushShellspawnCommandData(sockfd, type, NULL, 0); 291 else 292 pushShellspawnCommandData(sockfd, type, value, strlen(value) + 1); 293} 294 295static void pushShellspawnCommandFDs(int sockfd, shellspawn_cmd_type_t type, const int fds[3]) 296{ 297 struct shellspawn_cmd cmd; 298 char cmsgbuf[CMSG_SPACE(sizeof(int) * 3)]; 299 struct msghdr msg; 300 struct iovec iov; 301 struct cmsghdr *cmptr; 302 303 cmd.cmd = type; 304 cmd.data_length = 0; 305 306 iov.iov_base = &cmd; 307 308 memset(&msg, 0, sizeof(msg)); 309 msg.msg_control = cmsgbuf; 310 msg.msg_controllen = sizeof(cmsgbuf); 311 312 iov.iov_base = &cmd; 313 iov.iov_len = sizeof(cmd); 314 msg.msg_iov = &iov; 315 msg.msg_iovlen = 1; 316 317 cmptr = CMSG_FIRSTHDR(&msg); 318 cmptr->cmsg_len = CMSG_LEN(sizeof(int) * 3); 319 cmptr->cmsg_level = SOL_SOCKET; 320 cmptr->cmsg_type = SCM_RIGHTS; 321 memcpy(CMSG_DATA(cmptr), fds, sizeof(fds[0])*3); 322 323 if (sendmsg(sockfd, &msg, 0) < 0) 324 { 325 fprintf(stderr, "Error sending command to shellspawn: %s\n", strerror(errno)); 326 exit(EXIT_FAILURE); 327 } 328} 329 330static int _shSockfd = -1; 331static struct termios orig_termios; 332static int pty_master; 333static void signalHandler(int signo) 334{ 335 // printf("Received signal %d\n", signo); 336 337 // Forward window size changes 338 if (signo == SIGWINCH && pty_master != -1) 339 { 340 struct winsize win; 341 342 ioctl(0, TIOCGWINSZ, &win); 343 ioctl(pty_master, TIOCSWINSZ, &win); 344 } 345 346 // Foreground process loopkup in shellspawn doesn't work 347 // if we're not running in TTY mode, so shellspawn falls back 348 // to forwarding signals to the Bash subprocess. 349 // 350 // Hence we translate SIGINT to SIGTERM for user convenience, 351 // because Bash will not terminate on SIGINT. 352 if (pty_master == -1 && signo == SIGINT) 353 signo = SIGTERM; 354 355 pushShellspawnCommandData(_shSockfd, SHELLSPAWN_SIGNAL, &signo, sizeof(signo)); 356} 357 358static void shellLoop(int sockfd, int master) 359{ 360 struct sigaction sa; 361 struct pollfd pfds[3]; 362 const int fdcount = (master != -1) ? 3 : 1; // do we do pty proxying? 363 364 // Vars for signal handler 365 _shSockfd = sockfd; 366 pty_master = master; 367 368 memset(&sa, 0, sizeof(sa)); 369 sa.sa_handler = signalHandler; 370 sigfillset(&sa.sa_mask); 371 372 for (int i = 1; i < 32; i++) 373 sigaction(i, &sa, NULL); 374 375 pfds[2].fd = master; 376 pfds[2].events = POLLIN; 377 pfds[2].revents = 0; 378 pfds[1].fd = STDIN_FILENO; 379 pfds[1].events = POLLIN; 380 pfds[1].revents = 0; 381 pfds[0].fd = sockfd; 382 pfds[0].events = POLLIN; 383 pfds[0].revents = 0; 384 385 if (master != -1) 386 fcntl(master, F_SETFL, O_NONBLOCK); 387 //fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); 388 fcntl(sockfd, F_SETFL, O_NONBLOCK); 389 390 while (1) 391 { 392 char buf[4096]; 393 394 if (poll(pfds, fdcount, -1) < 0) 395 { 396 if (errno != EINTR) 397 { 398 perror("poll"); 399 break; 400 } 401 } 402 403 if (pfds[2].revents & POLLIN) 404 { 405 int rd; 406 do 407 { 408 rd = read(master, buf, sizeof(buf)); 409 if (rd > 0) 410 write(STDOUT_FILENO, buf, rd); 411 } 412 while (rd == sizeof(buf)); 413 } 414 415 if (pfds[1].revents & POLLIN) 416 { 417 int rd = 0; 418 do 419 { 420 if (ioctl(STDIN_FILENO, FIONREAD, &rd) < 0) { 421 perror("ioctl"); 422 exit(1); 423 } 424 if (rd > sizeof(buf)) { 425 rd = sizeof(buf); 426 } 427 rd = read(STDIN_FILENO, buf, rd); 428 if (rd > 0) 429 write(master, buf, rd); 430 else { 431 perror("read"); 432 exit(1); 433 } 434 } 435 while (rd == sizeof(buf)); 436 } 437 438 if (pfds[0].revents & (POLLHUP | POLLIN)) 439 { 440 int exitStatus; 441 442 if (read(sockfd, &exitStatus, sizeof(int)) == sizeof(int)) 443 exit(exitStatus); 444 else 445 exit(1); 446 } 447 } 448} 449 450static void restoreTermios(void) 451{ 452 tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); 453} 454 455// Glibc openpty() fails for me on Debian, because grantpty() fails to chown() the pty node with error code EPERM. 456// This is a more lenient version of openpty() that just works. 457static int openpty_darling(int* amaster, int* aslave, char* name_unused, const struct termios* tos, const struct winsize* wsz) 458{ 459 const char* slave_name; 460 461 *amaster = posix_openpt(O_RDWR); 462 if (*amaster == -1) 463 return -1; 464 465 grantpt(*amaster); 466 if (unlockpt(*amaster) < 0) 467 return -1; 468 469 slave_name = ptsname(*amaster); 470 *aslave = open(slave_name, O_RDWR | O_NOCTTY); 471 if (*aslave == -1) 472 return -1; 473 474 if (tos != NULL) 475 tcsetattr(*amaster, TCSANOW, tos); 476 if (wsz != NULL) 477 ioctl(*amaster, TIOCSWINSZ, wsz); 478 479 return 0; 480} 481 482static void setupPtys(int fds[3], int* master) 483{ 484 struct winsize win; 485 struct termios termios; 486 bool tty = true; 487 488 if (tcgetattr(STDIN_FILENO, &termios) < 0) 489 tty = false; 490 491 if (openpty_darling(master, &fds[0], NULL, &termios, NULL) < 0) 492 { 493 perror("openpty"); 494 exit(1); 495 } 496 fds[2] = fds[1] = fds[0]; 497 498 if (tty) 499 { 500 orig_termios = termios; 501 502 ioctl(0, TIOCGWINSZ, &win); 503 504 termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO); 505 termios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | 506 INPCK | ISTRIP | IXON | PARMRK); 507 termios.c_oflag &= ~OPOST; 508 termios.c_cc[VMIN] = 1; 509 termios.c_cc[VTIME] = 0; 510 511 if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) < 0) 512 { 513 perror("tcsetattr"); 514 exit(1); 515 } 516 ioctl(*master, TIOCSWINSZ, &win); 517 518 atexit(restoreTermios); 519 } 520} 521 522// Replace each quote character (') with the sequence '\'' 523// (copying the result to dest, if non-null) 524// and return the length of the result. 525static size_t escapeQuotes(char *dest, const char *src) 526{ 527 size_t len = 0; 528 529 for (; *src != 0; src++) 530 { 531 if (*src == '\'') 532 { 533 if (dest) 534 memcpy(&dest[len], "'\\''", 4); 535 len += 4; 536 } 537 else 538 { 539 if (dest) 540 dest[len] = *src; 541 len++; 542 } 543 } 544 if (dest) 545 dest[len] = 0; 546 547 return len; 548} 549 550int connectToShellspawn(void) 551{ 552 struct sockaddr_un addr; 553 int sockfd; 554 555 // Connect to the shellspawn daemon in the container 556 addr.sun_family = AF_UNIX; 557#if USE_LINUX_4_11_HACK 558 addr.sun_path[0] = '\0'; 559 560 strcpy(addr.sun_path, prefix); 561 strcat(addr.sun_path, SHELLSPAWN_SOCKPATH); 562#else 563 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s" SHELLSPAWN_SOCKPATH, prefix); 564#endif 565 566 sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 567 if (sockfd == -1) 568 { 569 fprintf(stderr, "Error creating a unix domain socket: %s\n", strerror(errno)); 570 exit(1); 571 } 572 573 if (connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) == -1) 574 { 575 fprintf(stderr, "Error connecting to shellspawn in the container (%s): %s\n", addr.sun_path, strerror(errno)); 576 exit(1); 577 } 578 579 return sockfd; 580} 581 582void setupShellspawnEnv(int sockfd) 583{ 584 static const char* skip_vars[] = { 585 "PATH", 586 "TMPDIR", 587 "HOME", 588 }; 589 590 char buffer2[4096]; 591 592 // Push environment variables 593 pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, 594 "PATH=/usr/bin:" 595 "/bin:" 596 "/usr/sbin:" 597 "/sbin:" 598 "/usr/local/bin"); 599 pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, "TMPDIR=/private/tmp"); 600 601 const char* login = NULL; 602 struct passwd* pw = getpwuid(geteuid()); 603 604 if (pw != NULL) 605 login = pw->pw_name; 606 607 if (!login) 608 login = getlogin(); 609 if (!login) 610 { 611 fprintf(stderr, "Cannot determine your user name\n"); 612 exit(1); 613 } 614 615 snprintf(buffer2, sizeof(buffer2), "HOME=/Users/%s", login); 616 pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, buffer2); 617 618 for (char** var_ptr = environ; *var_ptr != NULL; ++var_ptr) { 619 const char* var = *var_ptr; 620 const char* equal = strchr(var, '='); 621 size_t name_len = (equal != NULL) ? (size_t)(equal - var) : strlen(var); 622 bool skip_it = false; 623 624 for (size_t i = 0; i < sizeof(skip_vars) / sizeof(*skip_vars); ++i) { 625 if (strlen(skip_vars[i]) == name_len && strncmp(var, skip_vars[i], name_len) == 0) { 626 skip_it = true; 627 break; 628 } 629 } 630 631 if (skip_it) { 632 continue; 633 } 634 635 pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, var); 636 } 637} 638 639void setupWorkingDir(int sockfd) 640{ 641 char buffer2[4096]; 642 snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", g_workingDirectory); 643 pushShellspawnCommand(sockfd, SHELLSPAWN_CHDIR, buffer2); 644} 645 646void setupIDs(int sockfd) 647{ 648 int ids[2] = { g_originalUid, g_originalGid }; 649 pushShellspawnCommandData(sockfd, SHELLSPAWN_SETUIDGID, ids, sizeof(ids)); 650} 651 652void setupFDs(int fds[3], int* master) 653{ 654 *master = -1; 655 656 if (isatty(STDIN_FILENO)) 657 setupPtys(fds, master); 658 else 659 fds[0] = dup(STDIN_FILENO); // dup() because we close() after spawning 660 661 if (*master == -1 || !isatty(STDOUT_FILENO)) 662 fds[1] = STDOUT_FILENO; 663 if (*master == -1 || !isatty(STDERR_FILENO)) 664 fds[2] = STDERR_FILENO; 665} 666 667void spawnGo(int sockfd, int fds[3], int master) 668{ 669 pushShellspawnCommandFDs(sockfd, SHELLSPAWN_GO, fds); 670 close(fds[0]); 671 672 shellLoop(sockfd, master); 673 674 if (master != -1) 675 close(master); 676 close(sockfd); 677} 678 679void spawnShell(const char** argv) 680{ 681 size_t total_len = 0; 682 int count; 683 int sockfd; 684 char* buffer; 685 int fds[3], master; 686 687 if (argv != NULL) 688 { 689 for (count = 0; argv[count] != NULL; count++) 690 total_len += escapeQuotes(NULL, argv[count]); 691 692 buffer = malloc(total_len + count*3); 693 694 char *to = buffer; 695 for (int i = 0; argv[i] != NULL; i++) 696 { 697 if (to != buffer) 698 to = stpcpy(to, " "); 699 to = stpcpy(to, "'"); 700 to += escapeQuotes(to, argv[i]); 701 to = stpcpy(to, "'"); 702 } 703 } 704 else 705 buffer = NULL; 706 707 sockfd = connectToShellspawn(); 708 709 setupShellspawnEnv(sockfd); 710 711 // Push shell arguments 712 if (buffer != NULL) 713 { 714 pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c"); 715 pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer); 716 717 free(buffer); 718 } 719 720 setupWorkingDir(sockfd); 721 setupIDs(sockfd); 722 setupFDs(fds, &master); 723 spawnGo(sockfd, fds, master); 724} 725 726void spawnBinary(const char* binary, const char** argv) 727{ 728 int fds[3], master; 729 int sockfd; 730 731 sockfd = connectToShellspawn(); 732 setupShellspawnEnv(sockfd); 733 734 pushShellspawnCommand(sockfd, SHELLSPAWN_SETEXEC, binary); 735 736 for (; *argv != NULL; ++argv) 737 pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, *argv); 738 739 setupWorkingDir(sockfd); 740 setupIDs(sockfd); 741 setupFDs(fds, &master); 742 spawnGo(sockfd, fds, master); 743} 744 745void showHelp(const char* argv0) 746{ 747 fprintf(stderr, "This is Darling, translation layer for macOS software.\n\n"); 748 fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n\n"); 749 750 fprintf(stderr, "Usage:\n"); 751 fprintf(stderr, "\t%s <program-path> [arguments...]\n", argv0); 752 fprintf(stderr, "\t%s shell [arguments...]\n", argv0); 753 fprintf(stderr, "\t%s exec <program-path> [arguments...]\n", argv0); 754 fprintf(stderr, "\t%s shutdown\n", argv0); 755 fprintf(stderr, "\n"); 756 fprintf(stderr, "Environment variables:\n" 757 "DPREFIX - specifies the location of Darling prefix, defaults to ~/.darling\n"); 758} 759 760void showVersion(const char* argv0) { 761 fprintf(stderr, "%s " GIT_BRANCH " @ " GIT_COMMIT_HASH "\n", argv0); 762 fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n"); 763} 764 765void missingSetuidRoot(void) 766{ 767 char path[4096]; 768 int len; 769 770 len = readlink("/proc/self/exe", path, sizeof(path)-1); 771 if (len < 0) 772 strcpy(path, "darling"); 773 else 774 path[len] = '\0'; 775 776 fprintf(stderr, "Sorry, the `%s' binary is not setuid root, which is mandatory.\n", path); 777 fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n"); 778} 779 780pid_t spawnInitProcess(void) 781{ 782 pid_t pid; 783 int pipefd[2]; 784 char buffer[1]; 785 786 if (pipe(pipefd) == -1) 787 { 788 fprintf(stderr, "Cannot create a pipe for synchronization: %s\n", strerror(errno)); 789 exit(1); 790 } 791 792 if (unshare(CLONE_NEWUTS | CLONE_NEWIPC) != 0) 793 { 794 fprintf(stderr, "Cannot unshare UTS and IPC namespaces to create darling-init: %s\n", strerror(errno)); 795 exit(1); 796 } 797 798 pid = fork(); 799 800 if (pid < 0) 801 { 802 fprintf(stderr, "Cannot fork() to create darling-init: %s\n", strerror(errno)); 803 exit(1); 804 } 805 806 if (pid == 0) 807 { 808 // The child 809 810 char uid_str[21]; 811 char gid_str[21]; 812 char pipefd_str[21]; 813 814 snprintf(uid_str, sizeof(uid_str), "%d", g_originalUid); 815 snprintf(gid_str, sizeof(gid_str), "%d", g_originalGid); 816 snprintf(pipefd_str, sizeof(pipefd_str), "%d", pipefd[1]); 817 818 close(pipefd[0]); 819 820 execl(INSTALL_PREFIX "/bin/darlingserver", "darlingserver", prefix, uid_str, gid_str, pipefd_str, g_fixPermissions ? "1" : "0", NULL); 821 822 fprintf(stderr, "Failed to start darlingserver\n"); 823 exit(1); 824 } 825 826 // Wait for the child to drop UID/GIDs and unshare stuff 827 close(pipefd[1]); 828 read(pipefd[0], buffer, 1); 829 close(pipefd[0]); 830 831 /* 832 snprintf(idmap, sizeof(idmap), "/proc/%d/uid_map", pid); 833 834 file = fopen(idmap, "w"); 835 if (file != NULL) 836 { 837 fprintf(file, "0 %d 1\n", g_originalUid); // all users map to our user on the outside 838 fclose(file); 839 } 840 else 841 { 842 fprintf(stderr, "Cannot set uid_map for the init process: %s\n", strerror(errno)); 843 } 844 845 snprintf(idmap, sizeof(idmap), "/proc/%d/gid_map", pid); 846 847 file = fopen(idmap, "w"); 848 if (file != NULL) 849 { 850 fprintf(file, "0 %d 1\n", g_originalGid); // all groups map to our group on the outside 851 fclose(file); 852 } 853 else 854 { 855 fprintf(stderr, "Cannot set gid_map for the init process: %s\n", strerror(errno)); 856 } 857 */ 858 859 // Here's where we resume the child 860 // if we enable user namespaces 861 862 return pid; 863} 864 865void putInitPid(pid_t pidInit) 866{ 867 const char pidFile[] = "/.init.pid"; 868 char* pidPath; 869 FILE *fp; 870 871 pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile)); 872 strcpy(pidPath, prefix); 873 strcat(pidPath, pidFile); 874 875 seteuid(g_originalUid); 876 setegid(g_originalGid); 877 878 fp = fopen(pidPath, "w"); 879 880 seteuid(0); 881 setegid(0); 882 883 if (fp == NULL) 884 { 885 fprintf(stderr, "Cannot write out PID of the init process: %s\n", strerror(errno)); 886 return; 887 } 888 fprintf(fp, "%d", (int) pidInit); 889 fclose(fp); 890} 891 892char* defaultPrefixPath(void) 893{ 894 const char defaultPath[] = "/.darling"; 895 const char* home = getenv("HOME"); 896 char* buf; 897 898 if (!home) 899 { 900 fprintf(stderr, "Cannot detect your home directory!\n"); 901 return NULL; 902 } 903 904 buf = (char*) malloc(strlen(home) + sizeof(defaultPath)); 905 strcpy(buf, home); 906 strcat(buf, defaultPath); 907 908 return buf; 909} 910 911void createDir(const char* path) 912{ 913 struct stat st; 914 915 if (stat(path, &st) == 0) 916 { 917 if (!S_ISDIR(st.st_mode)) 918 { 919 fprintf(stderr, "%s already exists and is a file. Remove the file.\n", path); 920 exit(1); 921 } 922 } 923 else 924 { 925 if (errno == ENOENT) 926 { 927 if (mkdir(path, 0755) != 0) 928 { 929 fprintf(stderr, "Cannot create %s: %s\n", path, strerror(errno)); 930 exit(1); 931 } 932 } 933 else 934 { 935 fprintf(stderr, "Cannot access %s: %s\n", path, strerror(errno)); 936 exit(1); 937 } 938 } 939} 940 941void setupWorkdir() 942{ 943 char* workdir; 944 const char suffix[] = ".workdir"; 945 size_t len; 946 947 len = strlen(prefix); 948 workdir = (char*) alloca(len + sizeof(suffix)); 949 strcpy(workdir, prefix); 950 951 // Remove trailing / 952 while (workdir[len-1] == '/') 953 len--; 954 workdir[len] = '\0'; 955 956 strcat(workdir, suffix); 957 958 createDir(workdir); 959} 960 961int checkPrefixDir() 962{ 963 struct stat st; 964 965 if (stat(prefix, &st) == 0) 966 { 967 if (!S_ISDIR(st.st_mode)) 968 { 969 fprintf(stderr, "%s is a file. Remove the file.\n", prefix); 970 exit(1); 971 } 972 return 1; // OK 973 } 974 if (errno == ENOENT) 975 return 0; // not found 976 fprintf(stderr, "Cannot access %s: %s\n", prefix, strerror(errno)); 977 exit(1); 978} 979 980void setupPrefix() 981{ 982 char path[4096]; 983 size_t plen; 984 FILE* file; 985 struct passwd* passwd_entry; 986 987 const char* dirs[] = { 988 "/Volumes", 989 "/Applications", 990 "/usr", 991 "/usr/local", 992 "/usr/local/share", 993 "/private", 994 "/private/var", 995 "/private/var/log", 996 "/private/var/db", 997 "/private/etc", 998 "/var", 999 "/var/run", 1000 "/var/tmp", 1001 "/var/log" 1002 }; 1003 1004 fprintf(stderr, "Setting up a new Darling prefix at %s\n", prefix); 1005 1006 seteuid(g_originalUid); 1007 setegid(g_originalGid); 1008 1009 createDir(prefix); 1010 strcpy(path, prefix); 1011 strcat(path, "/"); 1012 plen = strlen(path); 1013 1014 for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++) 1015 { 1016 path[plen] = '\0'; 1017 strcat(path, dirs[i]); 1018 createDir(path); 1019 } 1020 1021 // create passwd, master.passwd, and group 1022 1023 passwd_entry = getpwuid(g_originalUid); 1024 if (!passwd_entry) { 1025 fprintf(stderr, "Failed to find Linux /etc/passwd entry for current user\n"); 1026 exit(1); 1027 } 1028 1029 path[plen] = '\0'; 1030 strcat(path, "/private/etc/passwd"); 1031 file = fopen(path, "w"); 1032 if (!file) { 1033 fprintf(stderr, "Failed to open /private/etc/passwd within the prefix\n"); 1034 exit(1); 1035 } 1036 1037 fprintf(file, 1038 "root:*:0:0:System Administrator:/var/root:/bin/sh\n" 1039 "%s:*:%d:%d:Darling User:/Users/%s:/bin/bash\n", 1040 passwd_entry->pw_name, 1041 passwd_entry->pw_uid, 1042 passwd_entry->pw_gid, 1043 passwd_entry->pw_name 1044 ); 1045 fclose(file); 1046 1047 path[plen] = '\0'; 1048 strcat(path, "/private/etc/master.passwd"); 1049 file = fopen(path, "w"); 1050 if (!file) { 1051 fprintf(stderr, "Failed to open /private/etc/master.passwd within the prefix\n"); 1052 exit(1); 1053 } 1054 1055 fprintf(file, 1056 "root:*:0:0::0:0:System Administrator:/var/root:/bin/sh\n" 1057 "%s:*:%d:%d::0:0:Darling User:/Users/%s:/bin/bash\n", 1058 passwd_entry->pw_name, 1059 passwd_entry->pw_uid, 1060 passwd_entry->pw_gid, 1061 passwd_entry->pw_name 1062 ); 1063 fclose(file); 1064 1065 path[plen] = '\0'; 1066 strcat(path, "/private/etc/group"); 1067 file = fopen(path, "w"); 1068 if (!file) { 1069 fprintf(stderr, "Failed to open /private/etc/group within the prefix\n"); 1070 exit(1); 1071 } 1072 1073 fprintf(file, 1074 "wheel:*:0:root,%s\n" 1075 "%s:*:%d:%s\n", 1076 passwd_entry->pw_name, 1077 passwd_entry->pw_name, 1078 passwd_entry->pw_gid, 1079 passwd_entry->pw_name 1080 ); 1081 fclose(file); 1082 1083 seteuid(0); 1084 setegid(0); 1085} 1086 1087pid_t getInitProcess() 1088{ 1089 const char pidFile[] = "/.init.pid"; 1090 char* pidPath; 1091 pid_t pid; 1092 int pid_i; 1093 FILE *fp; 1094 char procBuf[100]; 1095 char *exeBuf, *statusBuf; 1096 int uidMatch = 0, gidMatch = 0; 1097 1098 pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile)); 1099 strcpy(pidPath, prefix); 1100 strcat(pidPath, pidFile); 1101 1102 fp = fopen(pidPath, "r"); 1103 if (fp == NULL) 1104 return 0; 1105 1106 if (fscanf(fp, "%d", &pid_i) != 1) 1107 { 1108 fclose(fp); 1109 unlink(pidPath); 1110 return 0; 1111 } 1112 fclose(fp); 1113 pid = (pid_t) pid_i; 1114 1115 // Does the process exist? 1116 if (kill(pid, 0) == -1) 1117 { 1118 unlink(pidPath); 1119 return 0; 1120 } 1121 1122 // Is it actually an init process? 1123 snprintf(procBuf, sizeof(procBuf), "/proc/%d/comm", pid); 1124 fp = fopen(procBuf, "r"); 1125 if (fp == NULL) 1126 { 1127 unlink(pidPath); 1128 return 0; 1129 } 1130 1131 if (fscanf(fp, "%ms", &exeBuf) != 1) 1132 { 1133 fclose(fp); 1134 unlink(pidPath); 1135 return 0; 1136 } 1137 fclose(fp); 1138 1139 if (strcmp(exeBuf, "darlingserver") != 0) 1140 { 1141 unlink(pidPath); 1142 return 0; 1143 } 1144 free(exeBuf); 1145 1146 // Is it owned by the current user? 1147 if (g_originalUid != 0) 1148 { 1149 snprintf(procBuf, sizeof(procBuf), "/proc/%d/status", pid); 1150 fp = fopen(procBuf, "r"); 1151 if (fp == NULL) 1152 { 1153 unlink(pidPath); 1154 return 0; 1155 } 1156 1157 while (1) 1158 { 1159 statusBuf = NULL; 1160 size_t len; 1161 if (getline(&statusBuf, &len, fp) == -1) 1162 break; 1163 int rid, eid, sid, fid; 1164 if (sscanf(statusBuf, "Uid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4) 1165 { 1166 uidMatch = 1; 1167 uidMatch &= rid == g_originalUid; 1168 uidMatch &= eid == g_originalUid; 1169 uidMatch &= sid == g_originalUid; 1170 uidMatch &= fid == g_originalUid; 1171 } 1172 if (sscanf(statusBuf, "Gid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4) 1173 { 1174 gidMatch = 1; 1175 gidMatch &= rid == g_originalGid; 1176 gidMatch &= eid == g_originalGid; 1177 gidMatch &= sid == g_originalGid; 1178 gidMatch &= fid == g_originalGid; 1179 } 1180 free(statusBuf); 1181 } 1182 fclose(fp); 1183 1184 if (!uidMatch || !gidMatch) 1185 { 1186 unlink(pidPath); 1187 return 0; 1188 } 1189 } 1190 1191 return pid; 1192} 1193 1194void checkPrefixOwner() 1195{ 1196 struct stat st; 1197 1198 if (stat(prefix, &st) == 0) 1199 { 1200 if (g_originalUid != 0 && st.st_uid != g_originalUid) 1201 { 1202 fprintf(stderr, "You do not own the prefix directory.\n"); 1203 exit(1); 1204 } 1205 } 1206 else if (errno == EACCES) 1207 { 1208 fprintf(stderr, "You do not own the prefix directory.\n"); 1209 exit(1); 1210 } 1211}