diff options
Diffstat (limited to 'tools/gator/daemon/Command.cpp')
-rw-r--r-- | tools/gator/daemon/Command.cpp | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/tools/gator/daemon/Command.cpp b/tools/gator/daemon/Command.cpp new file mode 100644 index 000000000000..28d73cf5a905 --- /dev/null +++ b/tools/gator/daemon/Command.cpp @@ -0,0 +1,172 @@ +/** + * Copyright (C) ARM Limited 2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Command.h" + +#include <fcntl.h> +#include <pwd.h> +#include <stdio.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "Logging.h" +#include "SessionData.h" + +static int getUid(const char *const name, char *const shPath, const char *const tmpDir) { + // Lookups may fail when using a different libc or a statically compiled executable + char gatorTemp[32]; + snprintf(gatorTemp, sizeof(gatorTemp), "%s/gator_temp", tmpDir); + + const int fd = open(gatorTemp, 600, O_CREAT | O_CLOEXEC); + if (fd < 0) { + return -1; + } + close(fd); + + char cmd[128]; + snprintf(cmd, sizeof(cmd), "chown %s %s || rm %s", name, gatorTemp, gatorTemp); + + const int pid = fork(); + if (pid < 0) { + logg->logError(__FILE__, __LINE__, "fork failed"); + handleException(); + } + if (pid == 0) { + char cargv1[] = "-c"; + char *cargv[] = { + shPath, + cargv1, + cmd, + NULL, + }; + + execv(cargv[0], cargv); + exit(-1); + } + while ((waitpid(pid, NULL, 0) < 0) && (errno == EINTR)); + + struct stat st; + int result = -1; + if (stat(gatorTemp, &st) == 0) { + result = st.st_uid; + } + unlink(gatorTemp); + return result; +} + +static int getUid(const char *const name) { + // Look up the username + struct passwd *const user = getpwnam(name); + if (user != NULL) { + return user->pw_uid; + } + + + // Are we on Linux + char cargv0l[] = "/bin/sh"; + if ((access(cargv0l, X_OK) == 0) && (access("/tmp", W_OK) == 0)) { + return getUid(name, cargv0l, "/tmp"); + } + + // Are we on android + char cargv0a[] = "/system/bin/sh"; + if ((access(cargv0a, X_OK) == 0) && (access("/data", W_OK) == 0)) { + return getUid(name, cargv0a, "/data"); + } + + return -1; +} + +void *commandThread(void *) { + prctl(PR_SET_NAME, (unsigned long)&"gatord-command", 0, 0, 0); + + const char *const name = gSessionData->mCaptureUser == NULL ? "nobody" : gSessionData->mCaptureUser; + const int uid = getUid(name); + if (uid < 0) { + logg->logError(__FILE__, __LINE__, "Unable to lookup the user %s, please double check that the user exists", name); + handleException(); + } + + sleep(3); + + char buf[128]; + int pipefd[2]; + if (pipe_cloexec(pipefd) != 0) { + logg->logError(__FILE__, __LINE__, "pipe failed"); + handleException(); + } + + const int pid = fork(); + if (pid < 0) { + logg->logError(__FILE__, __LINE__, "fork failed"); + handleException(); + } + if (pid == 0) { + char cargv0l[] = "/bin/sh"; + char cargv0a[] = "/system/bin/sh"; + char cargv1[] = "-c"; + char *cargv[] = { + cargv0l, + cargv1, + gSessionData->mCaptureCommand, + NULL, + }; + + buf[0] = '\0'; + close(pipefd[0]); + + // Gator runs at a high priority, reset the priority to the default + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), 0) == -1) { + snprintf(buf, sizeof(buf), "setpriority failed"); + goto fail_exit; + } + + if (setuid(uid) != 0) { + snprintf(buf, sizeof(buf), "setuid failed"); + goto fail_exit; + } + + { + const char *const path = gSessionData->mCaptureWorkingDir == NULL ? "/" : gSessionData->mCaptureWorkingDir; + if (chdir(path) != 0) { + snprintf(buf, sizeof(buf), "Unable to cd to %s, please verify the directory exists and is accessable to %s", path, name); + goto fail_exit; + } + } + + execv(cargv[0], cargv); + cargv[0] = cargv0a; + execv(cargv[0], cargv); + snprintf(buf, sizeof(buf), "execv failed"); + + fail_exit: + if (buf[0] != '\0') { + const ssize_t bytes = write(pipefd[1], buf, sizeof(buf)); + // Can't do anything if this fails + (void)bytes; + } + + exit(-1); + } + + close(pipefd[1]); + const ssize_t bytes = read(pipefd[0], buf, sizeof(buf)); + if (bytes > 0) { + logg->logError(__FILE__, __LINE__, buf); + handleException(); + } + close(pipefd[0]); + + return NULL; +} |