Ejemplo n.º 1
0
static void mount_emulated_storage(int user_id) {
    const char *emulated_source = getenv("EMULATED_STORAGE_SOURCE");
    const char *emulated_target = getenv("EMULATED_STORAGE_TARGET");
    const char* legacy = getenv("EXTERNAL_STORAGE");

    if (!emulated_source || !emulated_target) {
        // No emulated storage is present
        return;
    }

    // Create a second private mount namespace for our process
    if (unshare(CLONE_NEWNS) < 0) {
        PLOGE("unshare");
        return;
    }

    if (mount("rootfs", "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
        PLOGE("mount rootfs as slave");
        return;
    }

    // /mnt/shell/emulated -> /storage/emulated
    if (mount(emulated_source, emulated_target, NULL, MS_BIND, NULL) < 0) {
        PLOGE("mount emulated storage");
    }

    char target_user[PATH_MAX];
    snprintf(target_user, PATH_MAX, "%s/%d", emulated_target, user_id);

    // /mnt/shell/emulated/<user> -> /storage/emulated/legacy
    if (mount(target_user, legacy, NULL, MS_BIND | MS_REC, NULL) < 0) {
        PLOGE("mount legacy path");
    }
}
Ejemplo n.º 2
0
static int socket_accept(int serv_fd) {
    struct timeval tv;
    fd_set fds;
    int fd, rc;

    /* Wait 20 seconds for a connection, then give up. */
    tv.tv_sec = 20;
    tv.tv_usec = 0;
    FD_ZERO(&fds);
    FD_SET(serv_fd, &fds);
    do {
        rc = select(serv_fd + 1, &fds, NULL, NULL, &tv);
    } while (rc < 0 && errno == EINTR);
    if (rc < 1) {
        PLOGE("select");
        return -1;
    }

    fd = accept(serv_fd, NULL, NULL);
    if (fd < 0) {
        PLOGE("accept");
        return -1;
    }

    return fd;
}
Ejemplo n.º 3
0
static void write_int(int fd, int val) {
    int written = write(fd, &val, sizeof(int));
    if (written != sizeof(int)) {
        PLOGE("unable to write int");
        exit(-1);
    }
}
Ejemplo n.º 4
0
static void allow(char *shell, mode_t mask)
{
    struct su_initiator *from = &su_from;
    struct su_request *to = &su_to;
    char *exe = NULL;

    umask(mask);
    send_intent(&su_from, &su_to, "", 1, 1);

    if (!strcmp(shell, "")) {
        strcpy(shell , "/system/bin/sh");
    }
    exe = strrchr (shell, '/') + 1;
    setresgid(to->uid, to->uid, to->uid);
    setresuid(to->uid, to->uid, to->uid);
    LOGD("%u %s executing %u %s using shell %s : %s", from->uid, from->bin,
            to->uid, to->command, shell, exe);
    if (strcmp(to->command, DEFAULT_COMMAND)) {
        execl(shell, exe, "-c", to->command, (char*)NULL);
    } else {
        execl(shell, exe, "-", (char*)NULL);
    }
    PLOGE("exec");
    exit(EXIT_SUCCESS);
}
Ejemplo n.º 5
0
static void populate_environment(const struct su_context *ctx)
{
    struct passwd *pw;
    char *val;

    if (ctx->to.keepenv)
        return;

    pw = getpwuid(ctx->to.uid);
    if (pw) {
        setenv("HOME", pw->pw_dir, 1);
        setenv("SHELL", ctx->to.shell, 1);
        if (ctx->to.login || ctx->to.uid) {
            setenv("USER", pw->pw_name, 1);
            setenv("LOGNAME", pw->pw_name, 1);
        }
    }

    if (ctx->sdk_version >= 14) {
        val = get_parent_env(&ctx->from, "LD_LIBRARY_PATH", sizeof("LD_LIBRARY_PATH") - 1);
        if (val)
            if (setenv("LD_LIBRARY_PATH", val, 1))
                PLOGE("setenv(LD_LIBRARY_PATH)");
    }
}
Ejemplo n.º 6
0
static void prepare_bind() {
	int ret = 0;
	//Check if there is a use to mount bind
	if(access("/system/xbin/su", R_OK) != 0)
		return;

	ret = mkdir("/dev/su", 0700);

	ret = copy_file("/sbin/su", "/dev/su/su", 0755);
	if(ret) {
		PLOGE("Failed to copy su");
		return;
	}
	chmod("/dev/su/su", 0755);

	ret = setfilecon("/dev/su/su", "u:object_r:system_file:s0");
	if(ret) {
		LOGE("Failed to set file context");
		return;
	}

	ret = mount("/dev/su/su", "/system/xbin/su", "", MS_BIND, NULL);
	if(ret) {
		LOGE("Failed to mount bind");
		return;
	}
}
Ejemplo n.º 7
0
static void socket_cleanup(struct su_context *ctx) {
    if (ctx && ctx->sock_path[0]) {
        if (unlink(ctx->sock_path))
            PLOGE("unlink (%s)", ctx->sock_path);
        ctx->sock_path[0] = 0;
    }
}
Ejemplo n.º 8
0
static void sighandler(int sig) {
	(void)sig;
    restore_stdin();

    // Assume we'll only be called before death
    // See note before sigaction() in set_stdin_raw()
    //
    // Now, close all standard I/O to cause the pumps
    // to exit so we can continue and retrieve the exit
    // code
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    // Put back all the default handlers
    struct sigaction act;
    int i;

    memset(&act, '\0', sizeof(act));
    act.sa_handler = SIG_DFL;
    for (i = 0; quit_signals[i]; i++) {
        if (sigaction(quit_signals[i], &act, NULL) < 0) {
            PLOGE("Error removing signal handler");
            continue;
        }
    }
}
Ejemplo n.º 9
0
static void socket_cleanup(void)
{
    if (socket_path[0]) {
        if (unlink(socket_path))
            PLOGE("unlink (%s)", socket_path);
        socket_path[0] = 0;
    }
}
Ejemplo n.º 10
0
static char *get_parent_env(const struct su_initiator *from, const char *var, size_t varlen)
{
    char path[PATH_MAX];
    char env[8129];
    char *val = NULL;
    char *p;
    int fd = -1;
    int len, rest, i, l;

    snprintf(path, sizeof(path), "/proc/%u/environ", from->pid);
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        PLOGE("Opening environment");
        return NULL;
    }

    rest = 0;
    do {
        len = read(fd, env + rest, sizeof(env) - rest);
        if (len < 0)
            PLOGE("Reading environment");
        if (len <= 0)
            break;
        len += rest;

        for (i = 0; i < len; i += l) {
            l = strnlen(env + i, len - i) + 1;
            if (i + l > len)
                break;

            if (!strncmp(var, env + i, varlen) && env[i + varlen] == '=') {
                p = env + i + varlen + 1;
                val = malloc(l - varlen - 1);
                if (val)
                    strncpy(val, p, l - varlen - 1);
                goto out;
            }
        }
        rest = len - i;
        memmove(env, env + i, rest);
    } while (1);
out:
    close(fd);

    return val;
}
Ejemplo n.º 11
0
void set_identity(unsigned int uid) {
    /*
     * Set effective uid back to root, otherwise setres[ug]id will fail
     * if uid isn't root.
     */
    if (seteuid(0)) {
        PLOGE("seteuid (root)");
        exit(EXIT_FAILURE);
    }
    if (setresgid(uid, uid, uid)) {
        PLOGE("setresgid (%u)", uid);
        exit(EXIT_FAILURE);
    }
    if (setresuid(uid, uid, uid)) {
        PLOGE("setresuid (%u)", uid);
        exit(EXIT_FAILURE);
    }
}
Ejemplo n.º 12
0
static void write_string(int fd, char* val) {
    int len = strlen(val);
    write_int(fd, len);
    int written = write(fd, val, len);
    if (written != len) {
        PLOGE("unable to write string");
        exit(-1);
    }
}
Ejemplo n.º 13
0
static int socket_create_temp(char *path, size_t len) {
    int fd;
    struct sockaddr_un sun;

    fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (fd < 0) {
        PLOGE("socket");
        return -1;
    }
    if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
        PLOGE("fcntl FD_CLOEXEC");
        goto err;
    }

    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_LOCAL;
    snprintf(path, len, "%s/.socket%d", REQUESTOR_CACHE_PATH, getpid());
    memset(sun.sun_path, 0, sizeof(sun.sun_path));
    snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path);

    /*
     * Delete the socket to protect from situations when
     * something bad occured previously and the kernel reused pid from that process.
     * Small probability, isn't it.
     */
    unlink(sun.sun_path);

    if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
        PLOGE("bind");
        goto err;
    }

    if (listen(fd, 1) < 0) {
        PLOGE("listen");
        goto err;
    }

    return fd;
err:
    close(fd);
    return -1;
}
Ejemplo n.º 14
0
static int run_daemon_child(int infd, int outfd, int errfd, int argc, char** argv) {
    if (-1 == dup2(outfd, STDOUT_FILENO)) {
        PLOGE("dup2 child outfd");
        exit(-1);
    }

    if (-1 == dup2(errfd, STDERR_FILENO)) {
        PLOGE("dup2 child errfd");
        exit(-1);
    }

    if (-1 == dup2(infd, STDIN_FILENO)) {
        PLOGE("dup2 child infd");
        exit(-1);
    }

    close(infd);
    close(outfd);
    close(errfd);

    return su_main_nodaemon(argc, argv);
}
Ejemplo n.º 15
0
static int socket_send_request(int fd, const struct su_context *ctx)
{
    size_t len;
    size_t bin_size, cmd_size;
    char *cmd;

#define write_token(fd, data)				\
do {							\
	uint32_t __data = htonl(data);			\
	size_t __count = sizeof(__data);		\
	size_t __len = write((fd), &__data, __count);	\
	if (__len != __count) {				\
		PLOGE("write(" #data ")");		\
		return -1;				\
	}						\
} while (0)

    write_token(fd, PROTO_VERSION);
    write_token(fd, PATH_MAX);
    write_token(fd, ARG_MAX);
    write_token(fd, ctx->from.uid);
    write_token(fd, ctx->to.uid);
    bin_size = strlen(ctx->from.bin) + 1;
    write_token(fd, bin_size);
    len = write(fd, ctx->from.bin, bin_size);
    if (len != bin_size) {
        PLOGE("write(bin)");
        return -1;
    }
    cmd = get_command(&ctx->to);
    cmd_size = strlen(cmd) + 1;
    write_token(fd, cmd_size);
    len = write(fd, cmd, cmd_size);
    if (len != cmd_size) {
        PLOGE("write(cmd)");
        return -1;
    }
    return 0;
}
Ejemplo n.º 16
0
static int socket_receive_result(int fd, char *result, ssize_t result_len) {
    ssize_t len;

    LOGD("waiting for user");
    len = read(fd, result, result_len-1);
    if (len < 0) {
        PLOGE("read(result)");
        return -1;
    }
    result[len] = '\0';

    return 0;
}
Ejemplo n.º 17
0
// TODO: leverage this with exec_log?
static int silent_run(char* command) {
    char *args[] = { "sh", "-c", command, NULL, };
    set_identity(0);
    pid_t pid;
    pid = fork();
    /* Parent */
    if (pid < 0) {
        PLOGE("fork");
        return -1;
    }
    else if (pid > 0) {
        return 0;
    }
    int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC);
    dup2(zero, 0);
    int null = open("/dev/null", O_WRONLY | O_CLOEXEC);
    dup2(null, 1);
    dup2(null, 2);
    execv(_PATH_BSHELL, args);
    PLOGE("exec am");
    _exit(EXIT_FAILURE);
    return -1;
}
Ejemplo n.º 18
0
static int socket_create_temp(void)
{
    static char buf[PATH_MAX];
    int fd;

    struct sockaddr_un sun;

    fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (fd < 0) {
        PLOGE("socket");
        return -1;
    }

    for (;;) {
        memset(&sun, 0, sizeof(sun));
        sun.sun_family = AF_LOCAL;
        strcpy(buf, SOCKET_PATH_TEMPLATE);
        socket_path = mktemp(buf);
        snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path);

        if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
            if (errno != EADDRINUSE) {
                PLOGE("bind");
                return -1;
            }
        } else {
            break;
        }
    }

    if (listen(fd, 1) < 0) {
        PLOGE("listen");
        return -1;
    }

    return fd;
}
Ejemplo n.º 19
0
static __attribute__ ((noreturn)) void allow(struct su_context *ctx) {
    char *arg0;
    int argc, err;

    umask(ctx->umask);
    // No send to UI accepted requests for shell and root users (they are in the log)
    // if( ctx->from.uid != AID_SHELL && ctx->from.uid != AID_ROOT ) {
        send_result(ctx, ALLOW);
    // }

    arg0 = strrchr (ctx->to.shell, '/');
    arg0 = (arg0) ? arg0 + 1 : ctx->to.shell;
    if (ctx->to.login) {
        int s = strlen(arg0) + 2;
        char *p = malloc(s);

        if (!p)
            exit(EXIT_FAILURE);

        *p = '-';
        strcpy(p + 1, arg0);
        arg0 = p;
    }

    populate_environment(ctx);
    set_identity(ctx->to.uid);

#define PARG(arg)                                    \
    (ctx->to.optind + (arg) < ctx->to.argc) ? " " : "",                    \
    (ctx->to.optind + (arg) < ctx->to.argc) ? ctx->to.argv[ctx->to.optind + (arg)] : ""

    LOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s",
            ctx->from.uid, ctx->from.bin,
            ctx->to.uid, get_command(&ctx->to), ctx->to.shell,
            arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5),
            (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : "");

    argc = ctx->to.optind;
    if (ctx->to.command) {
        ctx->to.argv[--argc] = ctx->to.command;
        ctx->to.argv[--argc] = "-c";
    }
    ctx->to.argv[--argc] = arg0;
    execv(ctx->to.shell, ctx->to.argv + argc);
    err = errno;
    PLOGE("exec");
    fprintf(stderr, "Cannot execute %s: %s\n", ctx->to.shell, strerror(err));
    exit(EXIT_FAILURE);
}
Ejemplo n.º 20
0
/**
 * Setup signal handlers trap signals which should result in program termination
 * so that we can restore the terminal to its normal state and retrieve the 
 * return code.
 */
static void setup_sighandlers(void) {
    struct sigaction act;
    int i;

    // Install the termination handlers
    // Note: we're assuming that none of these signal handlers are already trapped.
    // If they are, we'll need to modify this code to save the previous handler and
    // call it after we restore stdin to its previous state.
    memset(&act, '\0', sizeof(act));
    act.sa_handler = &sighandler;
    for (i = 0; quit_signals[i]; i++) {
        if (sigaction(quit_signals[i], &act, NULL) < 0) {
            PLOGE("Error installing signal handler");
            continue;
        }
    }
}
Ejemplo n.º 21
0
bool root_access(pid_t ppid, int *conn_flag)
{
    int fd;
    char filename[1024] = {'\0'};
    char cmdline[1024] = {'\0'};

    sprintf(filename, "/proc/%d/cmdline", ppid);

    fd = open(filename, O_RDONLY);
    if (-1 == fd)
    {
        PLOGE("unable to open %s", filename);
        return false;
    }

    if ( -1 == readline(fd, cmdline, sizeof(cmdline)-1) )
    {
        LOGE("can not read %s", cmdline);
        close(fd);
        return false;
    }

    if (strcmp(DEFAULT_SHELL, cmdline) == 0)
    {
        LOGD("===>>> it is started from shell.");
        *conn_flag = 0;
        return true;
    }
    else
    {
        int n = sizeof(white_list) / sizeof(white_list[0]);
        int i;
        for (i = 0; i < n; i++)
        {
            if (strcmp(cmdline, white_list[i]) == 0)
            {
                *conn_flag = 1;
                return true;
            }
        }
    }

    return false;
}
Ejemplo n.º 22
0
static void fork_for_samsung(void)
{
    // Samsung CONFIG_SEC_RESTRICT_SETUID wants the parent process to have
    // EUID 0, or else our setresuid() calls will be denied.  So make sure
    // all such syscalls are executed by a child process.
    int rv;

    switch (fork()) {
    case 0:
        return;
    case -1:
        PLOGE("fork");
        exit(1);
    default:
        if (wait(&rv) < 0) {
            exit(1);
        } else {
            exit(WEXITSTATUS(rv));
        }
    }
}
Ejemplo n.º 23
0
static int socket_receive_result(int serv_fd, char *result, ssize_t result_len)
{
    ssize_t len;
    
    for (;;) {
        int fd = socket_accept(serv_fd);
        if (fd < 0)
            return -1;

        len = read(fd, result, result_len-1);
        if (len < 0) {
            PLOGE("read(result)");
            return -1;
        }

        if (len > 0) {
            break;
        }
    }

    result[len] = '\0';

    return 0;
}
Ejemplo n.º 24
0
static __attribute__ ((noreturn)) void allow(struct su_context *ctx) {
    char *arg0;
    int argc, err;

    hacks_update_context(ctx);

    umask(ctx->umask);
    int send_to_app = 1;

    // no need to log if called by root
    if (ctx->from.uid == AID_ROOT)
        send_to_app = 0;

    // dumpstate (which logs to logcat/shell) will spam the crap out of the system with su calls
    if (strcmp("/system/bin/dumpstate", ctx->from.bin) == 0)
        send_to_app = 0;

    if (send_to_app)
        send_result(ctx, ALLOW);

    if(ctx->bind.from[0] && ctx->bind.to[0])
        allow_bind(ctx);

    if(ctx->init[0])
        allow_init(ctx);

    char *binary;
    argc = ctx->to.optind;
    if (ctx->to.command) {
        binary = ctx->to.shell;
        ctx->to.argv[--argc] = ctx->to.command;
        ctx->to.argv[--argc] = "-c";
    }
    else if (ctx->to.shell) {
        binary = ctx->to.shell;
    }
    else {
        if (ctx->to.argv[argc]) {
            binary = ctx->to.argv[argc++];
        }
        else {
            binary = DEFAULT_SHELL;
        }
    }

    arg0 = strrchr (binary, '/');
    arg0 = (arg0) ? arg0 + 1 : binary;
    if (ctx->to.login) {
        int s = strlen(arg0) + 2;
        char *p = malloc(s);

        if (!p)
            exit(EXIT_FAILURE);

        *p = '-';
        strcpy(p + 1, arg0);
        arg0 = p;
    }

    populate_environment(ctx);
    set_identity(ctx->to.uid);

#define PARG(arg)                                    \
    (argc + (arg) < ctx->to.argc) ? " " : "",                    \
    (argc + (arg) < ctx->to.argc) ? ctx->to.argv[argc + (arg)] : ""

    LOGD("%u %s executing %u %s using binary %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s",
            ctx->from.uid, ctx->from.bin,
            ctx->to.uid, get_command(&ctx->to), binary,
            arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5),
            (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : "");

	if(ctx->to.context && strcmp(ctx->to.context, "u:r:su_light:s0") == 0) {
		setexeccon(ctx->to.context);
	} else {
		setexeccon("u:r:su:s0");
    }

    ctx->to.argv[--argc] = arg0;
    execvp(binary, ctx->to.argv + argc);
    err = errno;
    PLOGE("exec");
    fprintf(stderr, "Cannot execute %s: %s\n", binary, strerror(err));
    exit(EXIT_FAILURE);
}
Ejemplo n.º 25
0
static __attribute__ ((noreturn)) void allow(const struct su_context *ctx)
{
    char *arg0;
    int argc, err;

    umask(ctx->umask);
    send_intent(ctx, "", 1, ACTION_RESULT);

    arg0 = strrchr (ctx->to.shell, '/');
    arg0 = (arg0) ? arg0 + 1 : ctx->to.shell;
    if (ctx->to.login) {
        int s = strlen(arg0) + 2;
        char *p = malloc(s);

        if (!p)
            exit(EXIT_FAILURE);

        *p = '-';
        strcpy(p + 1, arg0);
        arg0 = p;
    }

    /*
     * Set effective uid back to root, otherwise setres[ug]id will fail
     * if ctx->to.uid isn't root.
     */
    if (seteuid(0)) {
        PLOGE("seteuid (root)");
        exit(EXIT_FAILURE);
    }

    populate_environment(ctx);

    if (setresgid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) {
        PLOGE("setresgid (%u)", ctx->to.uid);
        exit(EXIT_FAILURE);
    }
    if (setresuid(ctx->to.uid, ctx->to.uid, ctx->to.uid)) {
        PLOGE("setresuid (%u)", ctx->to.uid);
        exit(EXIT_FAILURE);
    }

#define PARG(arg)									\
    (ctx->to.optind + (arg) < ctx->to.argc) ? " " : "",					\
    (ctx->to.optind + (arg) < ctx->to.argc) ? ctx->to.argv[ctx->to.optind + (arg)] : ""

    LOGD("%u %s executing %u %s using shell %s : %s%s%s%s%s%s%s%s%s%s%s%s%s%s",
            ctx->from.uid, ctx->from.bin,
            ctx->to.uid, get_command(&ctx->to), ctx->to.shell,
            arg0, PARG(0), PARG(1), PARG(2), PARG(3), PARG(4), PARG(5),
            (ctx->to.optind + 6 < ctx->to.argc) ? " ..." : "");

    argc = ctx->to.optind;
    if (ctx->to.command) {
        ctx->to.argv[--argc] = ctx->to.command;
        ctx->to.argv[--argc] = "-c";
    }
    ctx->to.argv[--argc] = arg0;
    execv(ctx->to.shell, ctx->to.argv + argc);
    err = errno;
    PLOGE("exec");
    fprintf(stderr, "Cannot execute %s: %s\n", ctx->to.shell, strerror(err));
    exit(EXIT_FAILURE);
}
Ejemplo n.º 26
0
static int daemon_accept(int fd) {
    is_daemon = 1;
    int pid = read_int(fd);
    LOGD("remote pid: %d", pid);
    char *pts_slave = read_string(fd);
    LOGD("remote pts_slave: %s", pts_slave);
    daemon_from_uid = read_int(fd);
    LOGD("remote uid: %d", daemon_from_uid);
    daemon_from_pid = read_int(fd);
    LOGD("remote req pid: %d", daemon_from_pid);

    struct ucred credentials;
    int ucred_length = sizeof(struct ucred);
    /* fill in the user data structure */
    if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length)) {
        LOGE("could obtain credentials from unix domain socket");
        exit(-1);
    }
    // if the credentials on the other side of the wire are NOT root,
    // we can't trust anything being sent.
    if (credentials.uid != 0) {
        daemon_from_uid = credentials.uid;
        pid = credentials.pid;
        daemon_from_pid = credentials.pid;
    }

    int mount_storage = read_int(fd);
    // The the FDs for each of the streams
    int infd  = recv_fd(fd);
    int outfd = recv_fd(fd);
    int errfd = recv_fd(fd);

    int argc = read_int(fd);
    if (argc < 0 || argc > 512) {
        LOGE("unable to allocate args: %d", argc);
        exit(-1);
    }
    LOGD("remote args: %d", argc);
    char** argv = (char**)malloc(sizeof(char*) * (argc + 1));
    argv[argc] = NULL;
    int i;
    for (i = 0; i < argc; i++) {
        argv[i] = read_string(fd);
    }

    // ack
    write_int(fd, 1);

    // Fork the child process. The fork has to happen before calling
    // setsid() and opening the pseudo-terminal so that the parent
    // is not affected
    int child = fork();
    if (child < 0) {
        // fork failed, send a return code and bail out
        PLOGE("unable to fork");
        write(fd, &child, sizeof(int));
        close(fd);
        return child;
    }

    if (child != 0) {
        // In parent, wait for the child to exit, and send the exit code
        // across the wire.
        int status, code;

        free(pts_slave);

        LOGD("waiting for child exit");
        if (waitpid(child, &status, 0) > 0) {
            code = WEXITSTATUS(status);
        }
        else {
            code = -1;
        }

        // Pass the return code back to the client
        LOGD("sending code");
        if (write(fd, &code, sizeof(int)) != sizeof(int)) {
            PLOGE("unable to write exit code");
        }

        close(fd);
        LOGD("child exited");
        return code;
    }

    // We are in the child now
    // Close the unix socket file descriptor
    close (fd);

    // Become session leader
    if (setsid() == (pid_t) -1) {
        PLOGE("setsid");
    }

    int ptsfd;
    if (pts_slave[0]) {
        // Opening the TTY has to occur after the
        // fork() and setsid() so that it becomes
        // our controlling TTY and not the daemon's
        ptsfd = open(pts_slave, O_RDWR);
        if (ptsfd == -1) {
            PLOGE("open(pts_slave) daemon");
            exit(-1);
        }

        if (infd < 0)  {
            LOGD("daemon: stdin using PTY");
            infd  = ptsfd;
        }
        if (outfd < 0) {
            LOGD("daemon: stdout using PTY");
            outfd = ptsfd;
        }
        if (errfd < 0) {
            LOGD("daemon: stderr using PTY");
            errfd = ptsfd;
        }
    } else {
        // If a TTY was sent directly, make it the CTTY.
        if (isatty(infd)) {
            ioctl(infd, TIOCSCTTY, 1);
        }
    }
    free(pts_slave);

#ifdef SUPERUSER_EMBEDDED
    if (mount_storage) {
        mount_emulated_storage(multiuser_get_user_id(daemon_from_uid));
    }
#endif

    return run_daemon_child(infd, outfd, errfd, argc, argv);
}
Ejemplo n.º 27
0
int run_daemon() {
    if (getuid() != 0 || getgid() != 0) {
        PLOGE("daemon requires root. uid/gid not root");
        return -1;
    }

	prepare();

    int fd;
    struct sockaddr_un sun;

    fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (fd < 0) {
        PLOGE("socket");
        return -1;
    }
    if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
        PLOGE("fcntl FD_CLOEXEC");
        goto err;
    }

    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_LOCAL;
    sprintf(sun.sun_path, "%s/server", REQUESTOR_DAEMON_PATH);

    /*
     * Delete the socket to protect from situations when
     * something bad occured previously and the kernel reused pid from that process.
     * Small probability, isn't it.
     */
    unlink(sun.sun_path);
    unlink(REQUESTOR_DAEMON_PATH);

    int previous_umask = umask(027);
    mkdir(REQUESTOR_DAEMON_PATH, 0777);

    memset(sun.sun_path, 0, sizeof(sun.sun_path));
    memcpy(sun.sun_path, "\0" "SUPERUSER", strlen("SUPERUSER") + 1);

    if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
        PLOGE("daemon bind");
        goto err;
    }

    chmod(REQUESTOR_DAEMON_PATH, 0755);
    chmod(sun.sun_path, 0777);

    umask(previous_umask);

    if (listen(fd, 10) < 0) {
        PLOGE("daemon listen");
        goto err;
    }

    int client;
    while ((client = accept(fd, NULL, NULL)) > 0) {
        if (fork_zero_fucks() == 0) {
            close(fd);
            return daemon_accept(client);
        }
        else {
            close(client);
        }
    }

    LOGE("daemon exiting");
err:
    close(fd);
    return -1;
}
Ejemplo n.º 28
0
static int from_init(struct su_initiator *from) {
    char path[PATH_MAX], exe[PATH_MAX];
    char args[4096], *argv0, *argv_rest;
    int fd;
    ssize_t len;
    int i;
    int err;

    from->uid = getuid();
    from->pid = getppid();

    if (is_daemon) {
        from->uid = daemon_from_uid;
        from->pid = daemon_from_pid;
    }

    /* Get the command line */
    snprintf(path, sizeof(path), "/proc/%u/cmdline", from->pid);
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        PLOGE("Opening command line");
        return -1;
    }
    len = read(fd, args, sizeof(args));
    err = errno;
    close(fd);
    if (len < 0 || len == sizeof(args)) {
        PLOGEV("Reading command line", err);
        return -1;
    }

    argv0 = args;
    argv_rest = NULL;
    for (i = 0; i < len; i++) {
        if (args[i] == '\0') {
            if (!argv_rest) {
                argv_rest = &args[i+1];
            } else {
                args[i] = ' ';
            }
        }
    }
    args[len] = '\0';

    if (argv_rest) {
        strncpy(from->args, argv_rest, sizeof(from->args));
        from->args[sizeof(from->args)-1] = '\0';
    } else {
        from->args[0] = '\0';
    }

    /* If this isn't app_process, use the real path instead of argv[0] */
    snprintf(path, sizeof(path), "/proc/%u/exe", from->pid);
    len = readlink(path, exe, sizeof(exe));
    if (len < 0) {
        PLOGE("Getting exe path");
        return -1;
    }
    exe[len] = '\0';
    if (strcmp(exe, "/system/bin/app_process")) {
        argv0 = exe;
    }

    strncpy(from->bin, argv0, sizeof(from->bin));
    from->bin[sizeof(from->bin)-1] = '\0';

    struct passwd *pw;
    pw = getpwuid(from->uid);
    if (pw && pw->pw_name) {
        strncpy(from->name, pw->pw_name, sizeof(from->name));
    }

    return 0;
}
Ejemplo n.º 29
0
/*
 * Receive a file descriptor from a Unix socket.
 * Contributed by @mkasick
 *
 * Returns the file descriptor on success, or -1 if a file
 * descriptor was not actually included in the message
 *
 * On error the function terminates by calling exit(-1)
 */
static int recv_fd(int sockfd) {
    // Need to receive data from the message, otherwise don't care about it.
    char iovbuf;

    struct iovec iov = {
        .iov_base = &iovbuf,
        .iov_len  = 1,
    };

    char cmsgbuf[CMSG_SPACE(sizeof(int))];

    struct msghdr msg = {
        .msg_iov        = &iov,
        .msg_iovlen     = 1,
        .msg_control    = cmsgbuf,
        .msg_controllen = sizeof(cmsgbuf),
    };

    if (recvmsg(sockfd, &msg, MSG_WAITALL) != 1) {
        goto error;
    }

    // Was a control message actually sent?
    switch (msg.msg_controllen) {
    case 0:
        // No, so the file descriptor was closed and won't be used.
        return -1;
    case sizeof(cmsgbuf):
        // Yes, grab the file descriptor from it.
        break;
    default:
        goto error;
    }

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);

    if (cmsg             == NULL                  ||
        cmsg->cmsg_len   != CMSG_LEN(sizeof(int)) ||
        cmsg->cmsg_level != SOL_SOCKET            ||
        cmsg->cmsg_type  != SCM_RIGHTS) {
error:
        LOGE("unable to read fd");
        exit(-1);
    }

    return *(int *)CMSG_DATA(cmsg);
}

/*
 * Send a file descriptor through a Unix socket.
 * Contributed by @mkasick
 *
 * On error the function terminates by calling exit(-1)
 *
 * fd may be -1, in which case the dummy data is sent,
 * but no control message with the FD is sent.
 */
static void send_fd(int sockfd, int fd) {
    // Need to send some data in the message, this will do.
    struct iovec iov = {
        .iov_base = "",
        .iov_len  = 1,
    };

    struct msghdr msg = {
        .msg_iov        = &iov,
        .msg_iovlen     = 1,
    };

    char cmsgbuf[CMSG_SPACE(sizeof(int))];

    if (fd != -1) {
        // Is the file descriptor actually open?
        if (fcntl(fd, F_GETFD) == -1) {
            if (errno != EBADF) {
                goto error;
            }
            // It's closed, don't send a control message or sendmsg will EBADF.
        } else {
            // It's open, send the file descriptor in a control message.
            msg.msg_control    = cmsgbuf;
            msg.msg_controllen = sizeof(cmsgbuf);

            struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);

            cmsg->cmsg_len   = CMSG_LEN(sizeof(int));
            cmsg->cmsg_level = SOL_SOCKET;
            cmsg->cmsg_type  = SCM_RIGHTS;

            *(int *)CMSG_DATA(cmsg) = fd;
        }
    }

    if (sendmsg(sockfd, &msg, 0) != 1) {
error:
        PLOGE("unable to send fd");
        exit(-1);
    }
}

static int read_int(int fd) {
    int val;
    int len = read(fd, &val, sizeof(int));
    if (len != sizeof(int)) {
        LOGE("unable to read int: %d", len);
        exit(-1);
    }
    return val;
}

static void write_int(int fd, int val) {
    int written = write(fd, &val, sizeof(int));
    if (written != sizeof(int)) {
        PLOGE("unable to write int");
        exit(-1);
    }
}

static char* read_string(int fd) {
    int len = read_int(fd);
    if (len > PATH_MAX || len < 0) {
        LOGE("invalid string length %d", len);
        exit(-1);
    }
    char* val = malloc(sizeof(char) * (len + 1));
    if (val == NULL) {
        LOGE("unable to malloc string");
        exit(-1);
    }
    val[len] = '\0';
    int amount = read(fd, val, len);
    if (amount != len) {
        LOGE("unable to read string");
        exit(-1);
    }
    return val;
}

static void write_string(int fd, char* val) {
    int len = strlen(val);
    write_int(fd, len);
    int written = write(fd, val, len);
    if (written != len) {
        PLOGE("unable to write string");
        exit(-1);
    }
}
Ejemplo n.º 30
0
int connect_daemon(int argc, char *argv[], int ppid) {
    int uid = getuid();
    int ptmx = -1;
    char pts_slave[PATH_MAX];

    struct sockaddr_un sun;

    // Open a socket to the daemon
    int socketfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (socketfd < 0) {
        PLOGE("socket");
        exit(-1);
    }
    if (fcntl(socketfd, F_SETFD, FD_CLOEXEC)) {
        PLOGE("fcntl FD_CLOEXEC");
        exit(-1);
    }

    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_LOCAL;
    sprintf(sun.sun_path, "%s/server", REQUESTOR_DAEMON_PATH);

    memset(sun.sun_path, 0, sizeof(sun.sun_path));
    memcpy(sun.sun_path, "\0" "SUPERUSER", strlen("SUPERUSER") + 1);

    if (0 != connect(socketfd, (struct sockaddr*)&sun, sizeof(sun))) {
        PLOGE("connect");
        exit(-1);
    }

    LOGD("connecting client %d", getpid());

    int mount_storage = getenv("MOUNT_EMULATED_STORAGE") != NULL;

    // Determine which one of our streams are attached to a TTY
    int atty = 0;

    // Send TTYs directly (instead of proxying with a PTY) if
    // the SUPERUSER_SEND_TTY environment variable is set.
    if (getenv("SUPERUSER_SEND_TTY") == NULL) {
        if (isatty(STDIN_FILENO))  atty |= ATTY_IN;
        if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT;
        if (isatty(STDERR_FILENO)) atty |= ATTY_ERR;
    }

    if (atty) {
        // We need a PTY. Get one.
        ptmx = pts_open(pts_slave, sizeof(pts_slave));
        if (ptmx < 0) {
            PLOGE("pts_open");
            exit(-1);
        }
    } else {
        pts_slave[0] = '\0';
    }

    // Send some info to the daemon, starting with our PID
    write_int(socketfd, getpid());
    // Send the slave path to the daemon
    // (This is "" if we're not using PTYs)
    write_string(socketfd, pts_slave);
    // User ID
    write_int(socketfd, uid);
    // Parent PID
    write_int(socketfd, ppid);
    write_int(socketfd, mount_storage);

    // Send stdin
    if (atty & ATTY_IN) {
        // Using PTY
        send_fd(socketfd, -1);
    } else {
        send_fd(socketfd, STDIN_FILENO);
    }

    // Send stdout
    if (atty & ATTY_OUT) {
        // Forward SIGWINCH
        watch_sigwinch_async(STDOUT_FILENO, ptmx);

        // Using PTY
        send_fd(socketfd, -1);
    } else {
        send_fd(socketfd, STDOUT_FILENO);
    }

    // Send stderr
    if (atty & ATTY_ERR) {
        // Using PTY
        send_fd(socketfd, -1);
    } else {
        send_fd(socketfd, STDERR_FILENO);
    }

    // Number of command line arguments
    write_int(socketfd, mount_storage ? argc - 1 : argc);

    // Command line arguments
    int i;
    for (i = 0; i < argc; i++) {
        if (i == 1 && mount_storage) {
            continue;
        }
        write_string(socketfd, argv[i]);
    }

    // Wait for acknowledgement from daemon
    read_int(socketfd);

    if (atty & ATTY_IN) {
        setup_sighandlers();
        pump_stdin_async(ptmx);
    }
    if (atty & ATTY_OUT) {
        pump_stdout_blocking(ptmx);
    }

    // Get the exit code
    int code = read_int(socketfd);
    close(socketfd);
    LOGD("client exited %d", code);

    return code;
}