From b926434fe6eb2bf240d56d525489ac49a7ffbbdc Mon Sep 17 00:00:00 2001 From: Ariel Abreu Date: Sat, 29 Apr 2023 10:29:51 -0400 Subject: [PATCH] Add `exec` subcommand to `darling` executable This subcommand allows you to execute a binary directly, without the need for a shell. --- src/shellspawn/shellspawn.c | 26 +++++- src/shellspawn/shellspawn.h | 3 +- src/startup/darling.c | 166 +++++++++++++++++++++++++----------- src/startup/darling.h | 7 ++ 4 files changed, 149 insertions(+), 53 deletions(-) diff --git a/src/shellspawn/shellspawn.c b/src/shellspawn/shellspawn.c index a6e7fa3ca..2f99825bc 100644 --- a/src/shellspawn/shellspawn.c +++ b/src/shellspawn/shellspawn.c @@ -48,7 +48,10 @@ void reapAll(void); int main(int argc, const char** argv) { - setupSigchild(); + // in order to read the exit status of the process, + // we have to allow it to become a zombie, which is prevented + // when we set the SIGCHLD signal to SA_NOCLDWAIT + //setupSigchild(); setupSocket(); listenForConnections(); @@ -134,6 +137,8 @@ void spawnShell(int fd) argv[0] = "/bin/bash"; argv[1] = "--login"; + char* alloc_exec = NULL; + // Read commands from client while (read_cmds) { @@ -238,6 +243,14 @@ void spawnShell(int fd) break; } + case SHELLSPAWN_SETEXEC: + { + argc = 0; + argv = realloc(argv, 0); + alloc_exec = param; + if (DBG) printf("setexec: %s\n", param); + break; + } } } @@ -270,7 +283,7 @@ void spawnShell(int fd) // In future, we may support spawning something else than Bash // and check the provided shell against /etc/shells - execv("/bin/bash", argv); + execv(alloc_exec ? alloc_exec : "/bin/bash", argv); rv = errno; write(pipefd[1], &rv, sizeof(rv)); @@ -279,6 +292,12 @@ void spawnShell(int fd) exit(EXIT_FAILURE); } + if (alloc_exec) + { + free(alloc_exec); + alloc_exec = NULL; + } + // Check that exec succeeded close(pipefd[1]); // close the write end if (read(pipefd[0], &rv, sizeof(rv)) == sizeof(rv)) @@ -379,7 +398,8 @@ void spawnShell(int fd) // Reap the child int wstatus; - waitpid(shell_pid, &wstatus, WEXITED); + if (waitpid(shell_pid, &wstatus, 0) != shell_pid) + perror("waitpid"); wstatus = WEXITSTATUS(wstatus); // Report exit code back to the client diff --git a/src/shellspawn/shellspawn.h b/src/shellspawn/shellspawn.h index 5c845e394..c9eb396e6 100644 --- a/src/shellspawn/shellspawn.h +++ b/src/shellspawn/shellspawn.h @@ -32,9 +32,10 @@ enum { SHELLSPAWN_ADDARG = 1, // add shell argument SHELLSPAWN_SETENV, // add env variable string SHELLSPAWN_CHDIR, - SHELLSPAWN_GO, // execute the shell now, must also contain file descriptors + SHELLSPAWN_GO, // execute the shell/executable now, must also contain file descriptors SHELLSPAWN_SIGNAL, // pass a signal from client SHELLSPAWN_SETUIDGID, // set virtual uid and gid + SHELLSPAWN_SETEXEC, // set the executable to spawn (instead of a shell). must be given before any ADDARG commands. }; struct __attribute__((packed)) shellspawn_cmd diff --git a/src/startup/darling.c b/src/startup/darling.c index 5039713a3..00d985dfb 100644 --- a/src/startup/darling.c +++ b/src/startup/darling.c @@ -1,7 +1,7 @@ /* This file is part of Darling. -Copyright (C) 2016-2020 Lubos Dolezel +Copyright (C) 2016-2023 Lubos Dolezel Darling is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -202,12 +202,21 @@ int main(int argc, char ** argv) } else { + bool doExec = strcmp(argv[1], "exec") == 0; + int argvIndex = doExec ? 2 : 1; + + if (doExec && argc <= 2) + { + printf("'exec' subcommand requires a binary to execute.\n"); + return 1; + } + char *fullPath; - char *path = realpath(argv[1], NULL); + char *path = realpath(argv[argvIndex], NULL); if (path == NULL) { - printf("'%s' is not a supported command or a file.\n", argv[1]); + printf("'%s' is not a supported command or a file.\n", argv[argvIndex]); return 1; } @@ -216,8 +225,12 @@ int main(int argc, char ** argv) strcat(fullPath, path); free(path); - argv[1] = fullPath; - spawnShell((const char**) &argv[1]); + argv[argvIndex] = fullPath; + + if (doExec) + spawnBinary(argv[argvIndex], (const char**) &argv[argvIndex]); + else + spawnShell((const char**) &argv[argvIndex]); } return 0; @@ -532,34 +545,10 @@ static size_t escapeQuotes(char *dest, const char *src) return len; } -void spawnShell(const char** argv) +int connectToShellspawn(void) { - size_t total_len = 0; - int count; - char buffer2[4096]; - int sockfd; struct sockaddr_un addr; - char* buffer; - - if (argv != NULL) - { - for (count = 0; argv[count] != NULL; count++) - total_len += escapeQuotes(NULL, argv[count]); - - buffer = malloc(total_len + count*3); - - char *to = buffer; - for (int i = 0; argv[i] != NULL; i++) - { - if (to != buffer) - to = stpcpy(to, " "); - to = stpcpy(to, "'"); - to += escapeQuotes(to, argv[i]); - to = stpcpy(to, "'"); - } - } - else - buffer = NULL; + int sockfd; // Connect to the shellspawn daemon in the container addr.sun_family = AF_UNIX; @@ -585,6 +574,13 @@ void spawnShell(const char** argv) exit(1); } + return sockfd; +} + +void setupShellspawnEnv(int sockfd) +{ + char buffer2[4096]; + // Push environment variables pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, "PATH=/usr/bin:" @@ -610,34 +606,38 @@ void spawnShell(const char** argv) snprintf(buffer2, sizeof(buffer2), "HOME=/Users/%s", login); pushShellspawnCommand(sockfd, SHELLSPAWN_SETENV, buffer2); +} - // Push shell arguments - if (buffer != NULL) - { - pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c"); - pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer); - - free(buffer); - } - +void setupWorkingDir(int sockfd) +{ + char buffer2[4096]; snprintf(buffer2, sizeof(buffer2), SYSTEM_ROOT "%s", g_workingDirectory); pushShellspawnCommand(sockfd, SHELLSPAWN_CHDIR, buffer2); +} +void setupIDs(int sockfd) +{ int ids[2] = { g_originalUid, g_originalGid }; pushShellspawnCommandData(sockfd, SHELLSPAWN_SETUIDGID, ids, sizeof(ids)); +} + +void setupFDs(int fds[3], int* master) +{ + *master = -1; - int fds[3], master = -1; - if (isatty(STDIN_FILENO)) - setupPtys(fds, &master); + setupPtys(fds, master); else - fds[0] = dup(STDIN_FILENO); // dup() because we close() a few lines below + fds[0] = dup(STDIN_FILENO); // dup() because we close() after spawning - if (master == -1 || !isatty(STDOUT_FILENO)) + if (*master == -1 || !isatty(STDOUT_FILENO)) fds[1] = STDOUT_FILENO; - if (master == -1 || !isatty(STDERR_FILENO)) + if (*master == -1 || !isatty(STDERR_FILENO)) fds[2] = STDERR_FILENO; +} +void spawnGo(int sockfd, int fds[3], int master) +{ pushShellspawnCommandFDs(sockfd, SHELLSPAWN_GO, fds); close(fds[0]); @@ -648,14 +648,82 @@ void spawnShell(const char** argv) close(sockfd); } +void spawnShell(const char** argv) +{ + size_t total_len = 0; + int count; + int sockfd; + char* buffer; + int fds[3], master; + + if (argv != NULL) + { + for (count = 0; argv[count] != NULL; count++) + total_len += escapeQuotes(NULL, argv[count]); + + buffer = malloc(total_len + count*3); + + char *to = buffer; + for (int i = 0; argv[i] != NULL; i++) + { + if (to != buffer) + to = stpcpy(to, " "); + to = stpcpy(to, "'"); + to += escapeQuotes(to, argv[i]); + to = stpcpy(to, "'"); + } + } + else + buffer = NULL; + + sockfd = connectToShellspawn(); + + setupShellspawnEnv(sockfd); + + // Push shell arguments + if (buffer != NULL) + { + pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, "-c"); + pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, buffer); + + free(buffer); + } + + setupWorkingDir(sockfd); + setupIDs(sockfd); + setupFDs(fds, &master); + spawnGo(sockfd, fds, master); +} + +void spawnBinary(const char* binary, const char** argv) +{ + int fds[3], master; + int sockfd; + + sockfd = connectToShellspawn(); + setupShellspawnEnv(sockfd); + + pushShellspawnCommand(sockfd, SHELLSPAWN_SETEXEC, binary); + + for (; *argv != NULL; ++argv) + pushShellspawnCommand(sockfd, SHELLSPAWN_ADDARG, *argv); + + setupWorkingDir(sockfd); + setupIDs(sockfd); + setupFDs(fds, &master); + spawnGo(sockfd, fds, master); +} + void showHelp(const char* argv0) { fprintf(stderr, "This is Darling, translation layer for macOS software.\n\n"); - fprintf(stderr, "Copyright (C) 2012-2020 Lubos Dolezel\n\n"); + fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n\n"); fprintf(stderr, "Usage:\n"); - fprintf(stderr, "\t%s program-path [arguments...]\n", argv0); + fprintf(stderr, "\t%s [arguments...]\n", argv0); fprintf(stderr, "\t%s shell [arguments...]\n", argv0); + fprintf(stderr, "\t%s exec [arguments...]\n", argv0); + fprintf(stderr, "\t%s shutdown\n", argv0); fprintf(stderr, "\n"); fprintf(stderr, "Environment variables:\n" "DPREFIX - specifies the location of Darling prefix, defaults to ~/.darling\n"); @@ -663,7 +731,7 @@ void showHelp(const char* argv0) void showVersion(const char* argv0) { fprintf(stderr, "%s " GIT_BRANCH " @ " GIT_COMMIT_HASH "\n", argv0); - fprintf(stderr, "Copyright (C) 2012-2020 Lubos Dolezel\n"); + fprintf(stderr, "Copyright (C) 2012-2023 Lubos Dolezel\n"); } void missingSetuidRoot(void) diff --git a/src/startup/darling.h b/src/startup/darling.h index ff0761dc3..1a49e2f50 100644 --- a/src/startup/darling.h +++ b/src/startup/darling.h @@ -40,7 +40,14 @@ int checkPrefixDir(void); // Creates the given directory, exit()ing if not possible void createDir(const char* path); +int connectToShellspawn(void); +void setupShellspawnEnv(int shellspawnFD); +void setupWorkingDir(int shellspawnFD); +void setupIDs(int shellspawnFD); +void setupFDs(int fds[3], int* master); +void spawnGo(int shellspawnFD, int fds[3], int master); void spawnShell(const char** argv); +void spawnBinary(const char* binary, const char** argv); // Set up some environment variables // As well as the working directory -- 2.51.2