Commit 3dfcc6b0 authored by topjohnwu's avatar topjohnwu

Checkout from seSuperuser/Superuser, leaving only native parts

- Checkout from https://github.com/seSuperuser/Superuser (commit: 69f84dd7a035b4a9f18dea69d9e0452bf0f73103)
- Move Superuser/Superuser/jni/su/* to root
- Move Superuser/jni/sqlite3/* to sqlite3
parents
/*
** Copyright 2010, Adam Shanks (@ChainsDD)
** Copyright 2008, Zinx Verituse (@zinxv)
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdarg.h>
#include "su.h"
/* intent actions */
#define ACTION_REQUEST "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".RequestActivity"
#define ACTION_NOTIFY "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".NotifyActivity"
#define ACTION_RESULT "broadcast", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".SuReceiver"
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
// TODO: leverage this with exec_log?
int silent_run(char* const args[]) {
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);
setenv("CLASSPATH", "/system/framework/am.jar", 1);
execv(args[0], args);
PLOGE("exec am");
_exit(EXIT_FAILURE);
return -1;
}
int get_owner_login_user_args(struct su_context *ctx, char* user, int user_len) {
int needs_owner_login_prompt = 0;
if (ctx->user.multiuser_mode == MULTIUSER_MODE_OWNER_MANAGED) {
if (0 != ctx->user.android_user_id) {
needs_owner_login_prompt = 1;
}
snprintf(user, user_len, "0");
}
else if (ctx->user.multiuser_mode == MULTIUSER_MODE_USER) {
snprintf(user, user_len, "%d", ctx->user.android_user_id);
}
else if (ctx->user.multiuser_mode == MULTIUSER_MODE_NONE) {
user[0] = '\0';
}
else {
snprintf(user, user_len, "0");
}
return needs_owner_login_prompt;
}
int send_result(struct su_context *ctx, policy_t policy) {
char binary_version[256];
sprintf(binary_version, "%d", VERSION_CODE);
char uid[256];
sprintf(uid, "%d", ctx->from.uid);
char desired_uid[256];
sprintf(desired_uid, "%d", ctx->to.uid);
char user[64];
get_owner_login_user_args(ctx, user, sizeof(user));
if (0 != ctx->user.android_user_id) {
char android_user_id[256];
sprintf(android_user_id, "%d", ctx->user.android_user_id);
char *user_result_command[] = {
AM_PATH,
ACTION_RESULT,
"--ei",
"binary_version",
binary_version,
"--es",
"from_name",
ctx->from.name,
"--es",
"desired_name",
ctx->to.name,
"--ei",
"uid",
uid,
"--ei",
"desired_uid",
desired_uid,
"--es",
"command",
get_command(&ctx->to),
"--es",
"action",
policy == ALLOW ? "allow" : "deny",
user[0] ? "--user" : NULL,
android_user_id,
NULL
};
silent_run(user_result_command);
}
char *result_command[] = {
AM_PATH,
ACTION_RESULT,
"--ei",
"binary_version",
binary_version,
"--es",
"from_name",
ctx->from.name,
"--es",
"desired_name",
ctx->to.name,
"--ei",
"uid",
uid,
"--ei",
"desired_uid",
desired_uid,
"--es",
"command",
get_command(&ctx->to),
"--es",
"action",
policy == ALLOW ? "allow" : "deny",
user[0] ? "--user" : NULL,
user,
NULL
};
return silent_run(result_command);
}
int send_request(struct su_context *ctx) {
// if su is operating in MULTIUSER_MODEL_OWNER,
// and the user requestor is not the owner,
// the owner needs to be notified of the request.
// so there will be two activities shown.
char user[64];
int needs_owner_login_prompt = get_owner_login_user_args(ctx, user, sizeof(user));
int ret;
if (needs_owner_login_prompt) {
char uid[256];
sprintf(uid, "%d", ctx->from.uid);
char android_user_id[256];
sprintf(android_user_id, "%d", ctx->user.android_user_id);
// in multiuser mode, the owner gets the su prompt
char *notify_command[] = {
AM_PATH,
ACTION_NOTIFY,
"--ei",
"caller_uid",
uid,
"--user",
android_user_id,
NULL
};
int ret = silent_run(notify_command);
if (ret) {
return ret;
}
}
char *request_command[] = {
AM_PATH,
ACTION_REQUEST,
"--es",
"socket",
ctx->sock_path,
user[0] ? "--user" : NULL,
user,
NULL
};
return silent_run(request_command);
}
/*
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <strings.h>
#include <stdint.h>
#include <pwd.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <sys/types.h>
#include <selinux/selinux.h>
#include <arpa/inet.h>
#include "binds.h"
int bind_foreach(bind_cb cb, void* arg) {
int res = 0;
char *str = NULL;
int fd = open("/data/su/binds", O_RDONLY);
if(fd<0)
return 1;
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
str = malloc(size);
if(read(fd, str, size) != size)
goto error;
char *base = str;
while(base < str+size) {
char *parse_src, *parse_dst;
int uid;
char *ptr = memchr(base, 0, size-(base-str));
if(ptr == NULL)
goto error;
sscanf(base, "%d", &uid);
parse_src = strchr(base, ':');
if(!parse_src)
goto error;
parse_src++;
parse_dst = strchr(parse_src, ':');
if(!parse_dst)
goto error;
*parse_dst = 0; // Split parse_src string
parse_dst++;
cb(arg, uid, parse_src, parse_dst);
base = ptr+1;
}
res = 1;
error:
if(str) free(str);
close(fd);
return res;
}
int bind_uniq_dst(const char *dst) {
static int _res;
static const char *_dst;
_res = 1;
_dst = dst;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
if(strcmp(dst, _dst) == 0)
_res = 0;
}
if(!bind_foreach(cb, NULL))
return 0;
return _res;
}
void bind_ls(int uid) {
static int _uid;
_uid=uid;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
if(_uid == 0 || _uid == 2000 || _uid == uid) {
fprintf(stderr, "%d %s => %s\n", uid, src, dst);
}
}
bind_foreach(cb, NULL);
}
int bind_remove(const char *path, int uid) {
static int _found = 0;
static const char *_path;
static int _fd;
static int _uid;
_path = path;
_found = 0;
_uid = uid;
unlink("/data/su/bind.new");
_fd = open("/data/su/bind.new", O_WRONLY|O_CREAT, 0600);
if(_fd<0)
return 0;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
//The one we want to drop
if(strcmp(dst, _path) == 0 &&
(_uid == 0 || _uid == 2000 || _uid == uid)) {
_found = 1;
return;
}
char *str = NULL;
int len = asprintf(&str, "%d:%s:%s", uid, src, dst);
write(_fd, str, len+1); //len doesn't include final \0 and we want to write it
free(str);
}
bind_foreach(cb, NULL);
close(_fd);
unlink("/data/su/bind");
rename("/data/su/bind.new", "/data/su/bind");
return _found;
}
int init_foreach(init_cb icb, void* arg) {
int res = 0;
char *str = NULL;
int fd = open("/data/su/init", O_RDONLY);
if(fd<0)
return 1;
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
str = malloc(size);
if(read(fd, str, size) != size)
goto error;
char *base = str;
while(base < str+size) {
char *parsed;
int uid;
char *ptr = memchr(base, 0, size-(base-str));
if(ptr == NULL)
goto error;
sscanf(base, "%d", &uid);
parsed = strchr(base, ':');
if(!parsed)
goto error;
parsed++;
icb(arg, uid, parsed);
base = ptr+1;
}
res = 1;
error:
if(str) free(str);
close(fd);
return res;
}
int init_uniq(const char *path) {
static int _res;
static const char *_path;
_res = 1;
_path = path;
auto void cb(void *arg, int uid, const char *path) {
if(strcmp(path, _path) == 0)
_res = 0;
}
if(!init_foreach(cb, NULL))
return 0;
return _res;
}
int init_remove(const char *path, int uid) {
static int _found = 0;
static const char *_path;
static int fd;
static int _uid;
_path = path;
_found = 0;
_uid = uid;
unlink("/data/su/init.new");
fd = open("/data/su/init.new", O_WRONLY|O_CREAT, 0600);
if(fd<0)
return 0;
auto void cb(void *arg, int uid, const char *path) {
//The one we want to drop
if(strcmp(path, _path) == 0 &&
(_uid == 0 || _uid == 2000 || uid == _uid)) {
_found = 1;
return;
}
char *str = NULL;
int len = asprintf(&str, "%d:%s", uid, path);
write(fd, str, len+1); //len doesn't include final \0 and we want to write it
free(str);
}
init_foreach(cb, NULL);
close(fd);
unlink("/data/su/init");
rename("/data/su/init.new", "/data/su/init");
return _found;
}
void init_ls(int uid) {
static int _uid;
_uid = uid;
auto void cb(void *arg, int uid, const char *path) {
if(_uid == 2000 || _uid == 0 || _uid == uid)
fprintf(stderr, "%d %s\n", uid, path);
}
init_foreach(cb, NULL);
}
/*
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef BINDSH
#define BINDS_H
typedef void (*bind_cb)(void *arg, int uid, const char *src, const char *dst);
extern int bind_foreach(bind_cb cb, void* arg);
extern int bind_uniq_dst(const char *dst);
extern int bind_remove(const char *path, int uid);
extern void bind_ls(int uid);
typedef void (*init_cb)(void *arg, int uid, const char *path);
extern int init_foreach(init_cb cb, void* arg);
extern int init_uniq(const char *dst);
extern int init_remove(const char *path, int uid);
extern void init_ls(int uid);
#endif /* BINDS_H */
This diff is collapsed.
/*
** Copyright 2013, Koushik Dutta (@koush)
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <sqlite3.h>
#include <time.h>
#include "su.h"
struct callback_data_t {
struct su_context *ctx;
policy_t policy;
};
static int database_callback(void *v, int argc, char **argv, char **azColName){
struct callback_data_t *data = (struct callback_data_t *)v;
int command_match = 0;
policy_t policy = DENY;
int i;
time_t until = 0;
for(i = 0; i < argc; i++) {
if (strcmp(azColName[i], "policy") == 0) {
if (argv[i] == NULL) {
policy = DENY;
}
if (strcmp(argv[i], "allow") == 0) {
policy = ALLOW;
}
else if (strcmp(argv[i], "interactive") == 0) {
policy = INTERACTIVE;
}
else {
policy = DENY;
}
}
else if (strcmp(azColName[i], "command") == 0) {
// null or empty command means to match all commands (whitelist all from uid)
command_match = argv[i] == NULL || strlen(argv[i]) == 0 || strcmp(argv[i], get_command(&(data->ctx->to))) == 0;
}
else if (strcmp(azColName[i], "until") == 0) {
if (argv[i] != NULL) {
until = atoi(argv[i]);
}
}
}
// check for command match
if (command_match) {
// also make sure this policy has not expired
if (until == 0 || until > time(NULL)) {
if (policy == DENY) {
data->policy = DENY;
return -1;
}
data->policy = ALLOW;
// even though we allow, continue, so we can see if there's another policy
// that denies...
}
}
return 0;
}
policy_t database_check(struct su_context *ctx) {
sqlite3 *db = NULL;
char query[512];
snprintf(query, sizeof(query), "select policy, until, command from uid_policy where uid=%d", ctx->from.uid);
int ret = sqlite3_open_v2(ctx->user.database_path, &db, SQLITE_OPEN_READONLY, NULL);
if (ret) {
LOGE("sqlite3 open failure: %d", ret);
sqlite3_close(db);
return INTERACTIVE;
}
int result;
char *err = NULL;
struct callback_data_t data;
data.ctx = ctx;
data.policy = INTERACTIVE;
ret = sqlite3_exec(db, query, database_callback, &data, &err);
sqlite3_close(db);
if (err != NULL) {
LOGE("sqlite3_exec: %s", err);
return DENY;
}
return data.policy;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "su.h"
#include "utils.h"
enum {
H_NO_CONTEXT = 0x0001,
};
static struct {
const char *package;
int flags;
int uid;
} apps_list[] = {
{ "com.keramidas.TitaniumBackup", H_NO_CONTEXT, },
};
void hacks_init() {
char oldCwd[512];
int i;
getcwd(oldCwd, sizeof(oldCwd));
chdir("/data/data");
for(i=0; i<(sizeof(apps_list)/sizeof(apps_list[0])); ++i) {
apps_list[i].uid = -1;
struct stat st_buf;
int ret = stat(apps_list[i].package, &st_buf);
LOGW("hacks: Testing (%s:%d:%d)", apps_list[i].package, ret, st_buf.st_uid);
if(ret)
continue;
apps_list[i].uid = st_buf.st_uid;
}
}
void hacks_update_context(struct su_context* ctxt) {
int i;
for(i=0; i<(sizeof(apps_list)/sizeof(apps_list[0])); ++i) {
LOGW("hacks: Testing (%s:%d), %d", apps_list[i].package, ctxt->from.uid);
if(apps_list[i].uid != ctxt->from.uid)
continue;
LOGW("hacks: Found app (%s:%d)", apps_list[i].package, ctxt->from.uid);
if(apps_list[i].flags & H_NO_CONTEXT) {
LOGW("hacks: Disabling context (%s:%d)", apps_list[i].package, ctxt->from.uid);
ctxt->to.context = NULL;
}
}
}
/*
* Copyright 2013, Tan Chee Eng (@tan-ce)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* pts.c
*
* Manages the pseudo-terminal driver on Linux/Android and provides some
* helper functions to handle raw input mode and terminal window resizing
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <errno.h>
#include <pthread.h>
#include "pts.h"
/**
* Helper functions
*/
// Ensures all the data is written out
static int write_blocking(int fd, char *buf, ssize_t bufsz) {
ssize_t ret, written;
written = 0;
do {
ret = write(fd, buf + written, bufsz - written);
if (ret == -1) return -1;
written += ret;
} while (written < bufsz);
return 0;
}
/**
* Pump data from input FD to output FD. If close_output is
* true, then close the output FD when we're done.
*/
static void pump_ex(int input, int output, int close_output) {
char buf[4096];
int len;
while ((len = read(input, buf, 4096)) > 0) {
if (write_blocking(output, buf, len) == -1) break;
}
close(input);
if (close_output) close(output);
}
/**
* Pump data from input FD to output FD. Will close the
* output FD when done.
*/
static void pump(int input, int output) {
pump_ex(input, output, 1);
}
static void* pump_thread(void* data) {
int* files = (int*)data;
int input = files[0];
int output = files[1];
pump(input, output);
free(data);
return NULL;
}
static void pump_async(int input, int output) {
pthread_t writer;
int* files = (int*)malloc(sizeof(int) * 2);
if (files == NULL) {
exit(-1);
}
files[0] = input;
files[1] = output;
pthread_create(&writer, NULL, pump_thread, files);
}
/**
* pts_open
*
* Opens a pts device and returns the name of the slave tty device.
*
* Arguments
* slave_name the name of the slave device
* slave_name_size the size of the buffer passed via slave_name
*
* Return Values
* on failure either -2 or -1 (errno set) is returned.
* on success, the file descriptor of the master device is returned.
*/
int pts_open(char *slave_name, size_t slave_name_size) {
int fdm;
char sn_tmp[256];
// Open master ptmx device
fdm = open("/dev/ptmx", O_RDWR);
if (fdm == -1) return -1;
// Get the slave name
if (ptsname_r(fdm, slave_name, slave_name_size-1)) {
close(fdm);
return -2;
}
slave_name[slave_name_size - 1] = '\0';
// Grant, then unlock
if (grantpt(fdm) == -1) {
close(fdm);
return -1;
}
if (unlockpt(fdm) == -1) {
close(fdm);
return -1;
}
return fdm;
}
// Stores the previous termios of stdin
static struct termios old_stdin;
static int stdin_is_raw = 0;
/**
* set_stdin_raw
*
* Changes stdin to raw unbuffered mode, disables echo,
* auto carriage return, etc.
*
* Return Value
* on failure -1, and errno is set
* on success 0
*/
int set_stdin_raw(void) {
struct termios new_termios;
// Save the current stdin termios
if (tcgetattr(STDIN_FILENO, &old_stdin) < 0) {
return -1;
}
// Start from the current settings
new_termios = old_stdin;
// Make the terminal like an SSH or telnet client
new_termios.c_iflag |= IGNPAR;
new_termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
new_termios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
new_termios.c_oflag &= ~OPOST;
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) < 0) {
return -1;
}
stdin_is_raw = 1;
return 0;
}
/**
* restore_stdin
*
* Restore termios on stdin to the state it was before
* set_stdin_raw() was called. If set_stdin_raw() was
* never called, does nothing and doesn't return an error.
*
* This function is async-safe.
*
* Return Value
* on failure, -1 and errno is set
* on success, 0
*/
int restore_stdin(void) {
if (!stdin_is_raw) return 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) {
return -1;
}
stdin_is_raw = 0;
return 0;
}
// Flag indicating whether the sigwinch watcher should terminate.
static volatile int closing_time = 0;
/**
* Thread process. Wait for a SIGWINCH to be received, then update
* the terminal size.
*/
static void *watch_sigwinch(void *data) {
sigset_t winch;
int sig;
int master = ((int *)data)[0];
int slave = ((int *)data)[1];
sigemptyset(&winch);
sigaddset(&winch, SIGWINCH);
do {
// Wait for a SIGWINCH
sigwait(&winch, &sig);
if (closing_time) break;
// Get the new terminal size
struct winsize w;
if (ioctl(master, TIOCGWINSZ, &w) == -1) {
continue;
}
// Set the new terminal size
ioctl(slave, TIOCSWINSZ, &w);
} while (1);
free(data);
return NULL;
}
/**
* watch_sigwinch_async
*
* After calling this function, if the application receives
* SIGWINCH, the terminal window size will be read from
* "input" and set on "output".
*
* NOTE: This function blocks SIGWINCH and spawns a thread.
* NOTE 2: This function must be called before any of the
* pump functions.
*
* Arguments
* master A file descriptor of the TTY window size to follow
* slave A file descriptor of the TTY window size which is
* to be set on SIGWINCH
*
* Return Value
* on failure, -1 and errno will be set. In this case, no
* thread has been spawned and SIGWINCH will not be
* blocked.
* on success, 0
*/
int watch_sigwinch_async(int master, int slave) {
pthread_t watcher;
int *files = (int *) malloc(sizeof(int) * 2);
if (files == NULL) {
return -1;
}
// Block SIGWINCH so sigwait can later receive it
sigset_t winch;
sigemptyset(&winch);
sigaddset(&winch, SIGWINCH);
if (sigprocmask(SIG_BLOCK, &winch, NULL) == -1) {
free(files);
return -1;
}
// Initialize some variables, then start the thread
closing_time = 0;
files[0] = master;
files[1] = slave;
int ret = pthread_create(&watcher, NULL, &watch_sigwinch, files);
if (ret != 0) {
free(files);
errno = ret;
return -1;
}
// Set the initial terminal size
raise(SIGWINCH);
return 0;
}
/**
* watch_sigwinch_cleanup
*
* Cause the SIGWINCH watcher thread to terminate
*/
void watch_sigwinch_cleanup(void) {
closing_time = 1;
raise(SIGWINCH);
}
/**
* pump_stdin_async
*
* Forward data from STDIN to the given FD
* in a seperate thread
*/
void pump_stdin_async(int outfd) {
// Put stdin into raw mode
set_stdin_raw();
// Pump data from stdin to the PTY
pump_async(STDIN_FILENO, outfd);
}
/**
* pump_stdout_blocking
*
* Forward data from the FD to STDOUT.
* Returns when the remote end of the FD closes.
*
* Before returning, restores stdin settings.
*/
void pump_stdout_blocking(int infd) {
// Pump data from stdout to PTY
pump_ex(infd, STDOUT_FILENO, 0 /* Don't close output when done */);
// Cleanup
restore_stdin();
watch_sigwinch_cleanup();
}
/*
* Copyright 2013, Tan Chee Eng (@tan-ce)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* pts.h
*
* Manages the pseudo-terminal driver on Linux/Android and provides some
* helper functions to handle raw input mode and terminal window resizing
*/
#ifndef _PTS_H_
#define _PTS_H_
/**
* pts_open
*
* Opens a pts device and returns the name of the slave tty device.
*
* Arguments
* slave_name the name of the slave device
* slave_name_size the size of the buffer passed via slave_name
*
* Return Values
* on failure either -2 or -1 (errno set) is returned.
* on success, the file descriptor of the master device is returned.
*/
int pts_open(char *slave_name, size_t slave_name_size);
/**
* set_stdin_raw
*
* Changes stdin to raw unbuffered mode, disables echo,
* auto carriage return, etc.
*
* Return Value
* on failure -1, and errno is set
* on success 0
*/
int set_stdin_raw(void);
/**
* restore_stdin
*
* Restore termios on stdin to the state it was before
* set_stdin_raw() was called. If set_stdin_raw() was
* never called, does nothing and doesn't return an error.
*
* This function is async-safe.
*
* Return Value
* on failure, -1 and errno is set
* on success, 0
*/
int restore_stdin(void);
/**
* watch_sigwinch_async
*
* After calling this function, if the application receives
* SIGWINCH, the terminal window size will be read from
* "input" and set on "output".
*
* NOTE: This function blocks SIGWINCH and spawns a thread.
*
* Arguments
* master A file descriptor of the TTY window size to follow
* slave A file descriptor of the TTY window size which is
* to be set on SIGWINCH
*
* Return Value
* on failure, -1 and errno will be set. In this case, no
* thread has been spawned and SIGWINCH will not be
* blocked.
* on success, 0
*/
int watch_sigwinch_async(int master, int slave);
/**
* watch_sigwinch_cleanup
*
* Cause the SIGWINCH watcher thread to terminate
*/
void watch_sigwinch_cleanup(void);
/**
* pump_stdin_async
*
* Forward data from STDIN to the given FD
* in a seperate thread
*/
void pump_stdin_async(int outfd);
/**
* pump_stdout_blocking
*
* Forward data from the FD to STDOUT.
* Returns when the remote end of the FD closes.
*
* Before returning, restores stdin settings.
*/
void pump_stdout_blocking(int infd);
#endif
/*
** Copyright 2013, Kevin Cernekee <cernekee@gmail.com>
**
** This was reverse engineered from an HTC "reboot" binary and is an attempt
** to remain bug-compatible with the original.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sqlite3.h>
#include <sys/reboot.h>
#include <android/log.h>
#define SETTINGS_DB "/data/data/com.android.providers.settings/databases/settings.db"
#define SCREEN_LOCK_STATUS "/data/misc/screen_lock_status"
int pattern_lock;
int sqlcallback(void *private, int n_columns, char **col_values, char **col_names)
{
pattern_lock = 0;
if (n_columns == 0 || !col_values[0])
return 0;
if (!strcmp(col_values[0], "1"))
pattern_lock = 1;
__android_log_print(ANDROID_LOG_INFO, NULL,
"sqlcallback %s = %s, pattern_locks= %d\n",
col_names[0], col_values[0], pattern_lock);
return 0;
}
int checkPatternLock(void)
{
sqlite3 *pDb = NULL;
char *errmsg = NULL;
if (sqlite3_open(SETTINGS_DB, &pDb) != 0) {
__android_log_print(ANDROID_LOG_ERROR, NULL,
"sqlite3_open error");
/* BUG? probably shouldn't call sqlite3_close() if open failed */
goto out;
}
if (sqlite3_exec(pDb,
"select value from system where name= \"lock_pattern_autolock\"",
sqlcallback, "checkPatternLock", &errmsg) != 0) {
__android_log_print(ANDROID_LOG_ERROR, NULL,
"SQL error: %s\n", errmsg);
sqlite3_free(errmsg);
goto out;
}
out:
sqlite3_close(pDb);
return 0;
}
int main(int argc, char **argv)
{
int no_sync = 0, power_off = 0;
int ret;
opterr = 0;
while ((ret = getopt(argc, argv, "np")) != -1) {
switch (ret) {
case 'n':
no_sync = 1;
break;
case 'p':
power_off = 1;
break;
case '?':
fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n",
argv[0]);
exit(1);
break;
}
}
if (argc > (optind + 1)) {
fprintf(stderr, "%s: too many arguments\n", argv[0]);
exit(1);
}
/* BUG: this should use optind */
if (argc > 1 && !strcmp(argv[1], "oem-78")) {
/* HTC RUU mode: "reboot oem-78" */
FILE *f;
char buf[5];
checkPatternLock();
f = fopen(SCREEN_LOCK_STATUS, "r");
if (!f) {
fputs("5\n", stderr);
exit(0);
}
fgets(buf, 5, f);
if (atoi(buf) == 1) {
if (pattern_lock != 0) {
fputs("1\n", stderr);
exit(0);
}
}
fputs("0\n", stderr);
}
if (!no_sync)
sync();
if (power_off) {
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF, 0);
} else if (optind >= argc) {
ret = reboot(LINUX_REBOOT_CMD_RESTART);
} else {
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, argv[optind]);
}
if (ret < 0) {
perror("reboot");
exit(1);
} else {
fputs("reboot returned\n", stderr);
exit(0);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
** Copyright 2010, Adam Shanks (@ChainsDD)
** Copyright 2008, Zinx Verituse (@zinxv)
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef SU_h
#define SU_h 1
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "su"
#ifndef AID_SHELL
#define AID_SHELL (get_shell_uid())
#endif
#ifndef AID_ROOT
#define AID_ROOT 0
#endif
#ifndef AID_SYSTEM
#define AID_SYSTEM (get_system_uid())
#endif
#ifndef AID_RADIO
#define AID_RADIO (get_radio_uid())
#endif
// CyanogenMod-specific behavior
#define CM_ROOT_ACCESS_DISABLED 0
#define CM_ROOT_ACCESS_APPS_ONLY 1
#define CM_ROOT_ACCESS_ADB_ONLY 2
#define CM_ROOT_ACCESS_APPS_AND_ADB 3
// DO NOT CHANGE LINE BELOW, java package name will always be the same
#define JAVA_PACKAGE_NAME "com.koushikdutta.superuser"
// If --rename-manifest-package is used in AAPT, this
// must be changed to correspond to the new APK package name
// See the two Android.mk files for more details.
#ifndef REQUESTOR
#define REQUESTOR JAVA_PACKAGE_NAME
#endif
// This is used if wrapping the fragment classes and activities
// with classes in another package. CM requirement.
#ifndef REQUESTOR_PREFIX
#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME
#endif
#define REQUESTOR_DATA_PATH "/data/data/"
#define REQUESTOR_FILES_PATH REQUESTOR_DATA_PATH REQUESTOR "/files"
#define REQUESTOR_USER_PATH "/data/user/"
#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
#define REQUESTOR_DAEMON_PATH REQUESTOR_CACHE_PATH ".daemon"
// there's no guarantee that the db or files are actually created named as such by
// SQLiteOpenHelper, etc. Though that is the behavior as of current.
// it is up to the Android application to symlink as appropriate.
#define REQUESTOR_DATABASE_PATH REQUESTOR "/databases/su.sqlite"
#define REQUESTOR_MULTIUSER_MODE REQUESTOR_FILES_PATH "/multiuser_mode"
#define DEFAULT_SHELL "/system/bin/sh"
#define xstr(a) str(a)
#define str(a) #a
#ifndef VERSION_CODE
#define VERSION_CODE 17
#endif
#define VERSION xstr(VERSION_CODE) " " REQUESTOR
#define PROTO_VERSION 1
struct su_initiator {
pid_t pid;
unsigned uid;
unsigned user;
char name[64];
char bin[PATH_MAX];
char args[4096];
};
struct su_request {
unsigned uid;
char name[64];
int login;
int keepenv;
char *shell;
char *context;
char *command;
char **argv;
int argc;
int optind;
};
struct su_user_info {
// the user in android userspace (multiuser)
// that invoked this action.
unsigned android_user_id;
// how su behaves with multiuser. see enum below.
int multiuser_mode;
// path to superuser directory. this is populated according
// to the multiuser mode.
// this is used to check uid/gid for protecting socket.
// this is used instead of database, as it is more likely
// to exist. db will not exist if su has never launched.
char base_path[PATH_MAX];
// path to su database. this is populated according
// to the multiuser mode.
char database_path[PATH_MAX];
};
struct su_bind {
const char *from;
const char *to;
};
struct su_context {
struct su_initiator from;
struct su_request to;
struct su_user_info user;
struct su_bind bind;
const char *init;
mode_t umask;
char sock_path[PATH_MAX];
};
// multiuser su behavior
typedef enum {
// only owner can su
MULTIUSER_MODE_OWNER_ONLY = 0,
// owner gets a su prompt
MULTIUSER_MODE_OWNER_MANAGED = 1,
// user gets a su prompt
MULTIUSER_MODE_USER = 2,
MULTIUSER_MODE_NONE = 3,
} multiuser_mode_t;
#define MULTIUSER_VALUE_OWNER_ONLY "owner"
#define MULTIUSER_VALUE_OWNER_MANAGED "managed"
#define MULTIUSER_VALUE_USER "user"
#define MULTIUSER_VALUE_NONE "none"
typedef enum {
INTERACTIVE = 0,
DENY = 1,
ALLOW = 2,
} policy_t;
extern policy_t database_check(struct su_context *ctx);
extern void set_identity(unsigned int uid);
extern int send_request(struct su_context *ctx);
extern int send_result(struct su_context *ctx, policy_t policy);
static inline char *get_command(const struct su_request *to)
{
if (to->command)
return to->command;
if (to->shell)
return to->shell;
char* ret = to->argv[to->optind];
if (ret)
return ret;
return DEFAULT_SHELL;
}
void exec_loge(const char* fmt, ...);
void exec_logw(const char* fmt, ...);
void exec_logd(const char* fmt, ...);
int run_daemon();
int connect_daemon(int argc, char *argv[], int ppid);
int su_main(int argc, char *argv[]);
int su_main_nodaemon(int argc, char *argv[]);
// for when you give zero fucks about the state of the child process.
// this version of fork understands you don't care about the child.
// deadbeat dad fork.
int fork_zero_fucks();
void hacks_init();
void hacks_update_context(struct su_context* ctxt);
// fallback to using /system/bin/log.
// can't use liblog.so because this is a static binary.
#ifndef LOGE
#define LOGE exec_loge
#endif
#ifndef LOGD
#define LOGD exec_logd
#endif
#ifndef LOGW
#define LOGW exec_logw
#endif
#if 0
#undef LOGE
#define LOGE(fmt,args...) fprintf(stderr, fmt, ##args)
#undef LOGD
#define LOGD(fmt,args...) fprintf(stderr, fmt, ##args)
#undef LOGW
#define LOGW(fmt,args...) fprintf(stderr, fmt, ##args)
#endif
#include <errno.h>
#include <string.h>
#define PLOGE(fmt,args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#define PLOGEV(fmt,err,args...) LOGE(fmt " failed with %d: %s", ##args, err, strerror(err))
#endif
/*
** Copyright 2012, The CyanogenMod Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
/* reads a file, making sure it is terminated with \n \0 */
char* read_file(const char *fn)
{
struct stat st;
char *data = NULL;
int fd = open(fn, O_RDONLY);
if (fd < 0) return data;
if (fstat(fd, &st)) goto oops;
data = malloc(st.st_size + 2);
if (!data) goto oops;
if (read(fd, data, st.st_size) != st.st_size) goto oops;
close(fd);
data[st.st_size] = '\n';
data[st.st_size + 1] = 0;
return data;
oops:
close(fd);
if (data) free(data);
return NULL;
}
int get_property(const char *data, char *found, const char *searchkey, const char *not_found)
{
char *key, *value, *eol, *sol, *tmp;
if (data == NULL) goto defval;
int matched = 0;
sol = strdup(data);
while((eol = strchr(sol, '\n'))) {
key = sol;
*eol++ = 0;
sol = eol;
value = strchr(key, '=');
if(value == 0) continue;
*value++ = 0;
while(isspace(*key)) key++;
if(*key == '#') continue;
tmp = value - 2;
while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
while(isspace(*value)) value++;
tmp = eol - 2;
while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
if (strncmp(searchkey, key, strlen(searchkey)) == 0) {
matched = 1;
break;
}
}
int len;
if (matched) {
len = strlen(value);
if (len >= PROPERTY_VALUE_MAX)
return -1;
memcpy(found, value, len + 1);
} else goto defval;
return len;
defval:
len = strlen(not_found);
memcpy(found, not_found, len + 1);
return len;
}
/*
* Fast version of get_property which purpose is to check
* whether the property with given prefix exists.
*
* Assume nobody is stupid enough to put a propery with prefix ro.cm.version
* in his build.prop on a non-CM ROM and comment it out.
*/
int check_property(const char *data, const char *prefix)
{
if (!data)
return 0;
return strstr(data, prefix) != NULL;
}
/*
** Copyright 2012, The CyanogenMod Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef _UTILS_H_
#define _UTILS_H_
#ifndef PROPERTY_VALUE_MAX
#define PROPERTY_VALUE_MAX 92
#endif
/* reads a file, making sure it is terminated with \n \0 */
extern char* read_file(const char *fn);
extern int get_property(const char *data, char *found, const char *searchkey,
const char *not_found);
extern int check_property(const char *data, const char *prefix);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment