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
This commit is contained in:
topjohnwu 2018-07-18 18:12:47 +08:00
commit 3dfcc6b0be
17 changed files with 151642 additions and 0 deletions

208
activity.c Normal file
View File

@ -0,0 +1,208 @@
/*
** 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);
}

246
binds.c Normal file
View File

@ -0,0 +1,246 @@
/*
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);
}

33
binds.h Normal file
View File

@ -0,0 +1,33 @@
/*
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 */

826
daemon.c Normal file
View File

@ -0,0 +1,826 @@
/*
** 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.
*/
#define _GNU_SOURCE /* for unshare() */
#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 <stdint.h>
#include <pwd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <stdarg.h>
#include <sys/types.h>
#include <pthread.h>
#include <sched.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <selinux/selinux.h>
#ifdef SUPERUSER_EMBEDDED
#include <cutils/multiuser.h>
#endif
#include "binds.h"
#include "su.h"
#include "utils.h"
#include "pts.h"
int is_daemon = 0;
int daemon_from_uid = 0;
int daemon_from_pid = 0;
// Constants for the atty bitfield
#define ATTY_IN 1
#define ATTY_OUT 2
#define ATTY_ERR 4
/*
* 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);
}
}
#ifdef SUPERUSER_EMBEDDED
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");
}
}
#endif
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);
}
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]) {
//Check pts_slave file is owned by daemon_from_uid
{
struct stat stbuf;
int res = stat(pts_slave, &stbuf);
if(res) {
PLOGE("stat(pts_slave) daemon");
exit(-1);
}
//If caller is not root, ensure the owner of pts_slave is the caller
if(stbuf.st_uid != credentials.uid &&
credentials.uid != 0) {
PLOGE("Wrong permission of pts_slave");
exit(-1);
}
}
// 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);
}
//Check we haven't been fooled
{
struct stat stbuf;
int res = fstat(ptsfd, &stbuf);
if(res) {
//If we have been fooled DO NOT WRITE ANYTHING
_exit(2);
}
if(stbuf.st_uid != credentials.uid &&
credentials.uid != 0) {
_exit(2);
}
}
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);
}
static int copy_file(const char* src, const char* dst, int mode) {
int ifd = open(src, O_RDONLY);
if(ifd<0)
return 1;
if(mode == 0) {
struct stat stbuf;
if(fstat(ifd, &stbuf))
return 1;
mode = stbuf.st_mode & 0777;
LOGE("File %s found mode %o", src, mode);
}
int ofd = open(dst, O_WRONLY|O_CREAT, mode);
if(ofd<0)
return 1;
size_t s = lseek(ifd, 0, SEEK_END);
if(s<0)
return 1;
lseek(ifd, 0, SEEK_SET);
int ret = sendfile(ofd, ifd, NULL, s);
if(ret<0)
return 1;
close(ofd);
close(ifd);
return 0;
}
static void prepare_su_bind() {
int ret = 0;
//Check if there is a use to mount bind
if(access("/system/xbin/su", R_OK) != 0)
return;
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;
}
}
static void prepare_binds() {
mkdir("/data/su", 0700);
static int i = 0;
auto void cb(void *arg, int uid, const char *src, const char *dst) {
int ret = 0;
char *tmpfile = NULL;
asprintf(&tmpfile, "/dev/su/bind%d", i++);
struct stat stbuf;
ret = stat(src, &stbuf);
if(ret) {
free(tmpfile);
LOGE("Failed to stat src %s file", src);
return;
}
//Only shell uid is allowed to bind files not his own
if(uid != 2000 && uid != stbuf.st_uid) {
LOGE("File %s has wrong owner: %d vs %d", src, uid, stbuf.st_uid);
return;
}
ret = copy_file(src, tmpfile, 0);
if(ret) {
free(tmpfile);
PLOGE("Failed to copy su");
return;
}
chmod(tmpfile, stbuf.st_mode);
ret = setfilecon(tmpfile, "u:object_r:system_file:s0");
if(ret) {
LOGE("Failed to set file context");
return;
}
ret = mount(tmpfile, dst, "", MS_BIND, NULL);
if(ret) {
LOGE("Failed to mount bind");
return;
}
}
bind_foreach(cb, NULL);
}
static void do_init() {
auto void cb(void *arg, int uid, const char *path) {
int ret = 0;
int p = fork();
if(p)
return;
while(access("/system/bin/sh", R_OK)) sleep(1);
ret = setexeccon("u:r:su:s0");
execl(path, path, NULL);
LOGE("Failed to execute %s. Trying as shell script, ret = %d", path, ret);
ret = setexeccon("u:r:su:s0");
execl("/system/bin/sh", "/system/bin/sh", path, NULL);
LOGE("Failed to execute %s as shell script", path);
_exit(1);
}
init_foreach(cb, NULL);
}
static void prepare() {
setfscreatecon("u:object_r:su_daemon:s0");
mkdir("/dev/su", 0700);
prepare_su_bind();
prepare_binds();
do_init();
setfscreatecon(NULL);
}
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;
}
// List of signals which cause process termination
static int quit_signals[] = { SIGALRM, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 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;
}
}
}
/**
* 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;
}
}
}
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;
}

105
db.c Normal file
View File

@ -0,0 +1,105 @@
/*
** 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;
}

49
hacks.c Normal file
View File

@ -0,0 +1,49 @@
#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;
}
}
}

333
pts.c Normal file
View File

@ -0,0 +1,333 @@
/*
* 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();
}

116
pts.h Normal file
View File

@ -0,0 +1,116 @@
/*
* 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

145
reboot/reboot.c Normal file
View File

@ -0,0 +1,145 @@
/*
** 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);
}
}

3163
sqlite3/shell.c Normal file

File diff suppressed because it is too large Load Diff

137414
sqlite3/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

7160
sqlite3/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

447
sqlite3/sqlite3ext.h Normal file
View File

@ -0,0 +1,447 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
#include "sqlite3.h"
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each others' shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
};
/*
** The following macros redefine the API routines so that they are
** redirected throught the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#ifndef SQLITE_CORE
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
#endif /* SQLITE_CORE */
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
#endif /* _SQLITE3EXT_H_ */

1034
su.c Normal file

File diff suppressed because it is too large Load Diff

221
su.h Normal file
View File

@ -0,0 +1,221 @@
/*
** 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

112
utils.c Normal file
View File

@ -0,0 +1,112 @@
/*
** 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;
}

30
utils.h Normal file
View File

@ -0,0 +1,30 @@
/*
** 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