Command.cpp 3.9 KB
Newer Older
Abhijith PA's avatar
Abhijith PA committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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;
}