Magisk/native/jni/core/db.cpp
topjohnwu b1afd554fc Application Component Granularity MagiskHide
Before switching to the new MagiskHide implementation (APK inotify),
logcat parsing provides us lots of information to target a process.
We were targeting components so that apps with multi-processes
can still be hidden properly.

After switching to the new implementation, our granularity is limited
to the UID of the process. This is especially dangerous since Android
allow apps signed with the same signature to share UIDs, and many system
apps utilize this for elevated permissions for some services.

This commit introduces process name matching. We could not blanketly
target an UID, so the workaround is to verify its process name before
unmounting.

The tricky thing is that any app developer is allowed to name the
process of its component to whatever they want; there is no 'one
rule to catch them all' to target a specific package. As a result,
Magisk Manager is updated to scan through all components of all apps,
and show different processes of the same app, each as a separate
hide target in the list.

The hide target database also has to be updated accordingly.
Each hide target is now a <package name, process name> pair. The
magiskhide CLI and Magisk Manager is updated to support this new
target format.
2019-03-01 17:08:08 -05:00

324 lines
7.6 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <magisk.h>
#include <db.h>
#include <daemon.h>
#define DB_VERSION 8
static sqlite3 *mDB = nullptr;
db_strings::db_strings() {
memset(data, 0, sizeof(data));
}
char *db_strings::operator[](const char *key) {
return data[getKeyIdx(key)];
}
const char *db_strings::operator[](const char *key) const {
return data[getKeyIdx(key)];
}
char *db_strings::operator[](const int idx) {
return data[idx];
}
const char *db_strings::operator[](const int idx) const {
return data[idx];
}
int db_strings::getKeyIdx(const char *key) const {
int idx = DB_STRING_NUM;
for (int i = 0; i < DB_STRING_NUM; ++i) {
if (strcmp(key, DB_STRING_KEYS[i]) == 0)
idx = i;
}
return idx;
}
db_settings::db_settings() : data {
ROOT_ACCESS_APPS_AND_ADB,
MULTIUSER_MODE_OWNER_ONLY,
NAMESPACE_MODE_REQUESTER,
1
} {}
int &db_settings::operator[](const int idx) {
return data[idx];
}
const int &db_settings::operator[](const int idx) const {
return data[idx];
}
int &db_settings::operator[](const char *key) {
return data[getKeyIdx(key)];
}
const int &db_settings::operator[](const char *key) const {
return data[getKeyIdx(key)];
}
int db_settings::getKeyIdx(const char *key) const {
int idx = DB_SETTINGS_NUM;
for (int i = 0; i < DB_SETTINGS_NUM; ++i) {
if (strcmp(key, DB_SETTING_KEYS[i]) == 0)
idx = i;
}
return idx;
}
static int ver_cb(void *ver, int, char **data, char **) {
*((int *) ver) = atoi(data[0]);
return 0;
}
#define err_ret(e) if (e) return e;
static char *open_and_init_db(sqlite3 *&db) {
int ret = sqlite3_open_v2(MAGISKDB, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
if (ret)
return strdup(sqlite3_errmsg(db));
int ver;
bool upgrade = false;
char *err;
sqlite3_exec(db, "PRAGMA user_version", ver_cb, &ver, &err);
err_ret(err);
if (ver > DB_VERSION) {
// Don't support downgrading database
sqlite3_close(db);
return nullptr;
}
if (ver < 3) {
// Policies
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS policies "
"(uid INT, package_name TEXT, policy INT, until INT, "
"logging INT, notification INT, PRIMARY KEY(uid))",
nullptr, nullptr, &err);
err_ret(err);
// Logs
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS logs "
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, "
"to_uid INT, action INT, time INT, command TEXT)",
nullptr, nullptr, &err);
err_ret(err);
// Settings
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS settings "
"(key TEXT, value INT, PRIMARY KEY(key))",
nullptr, nullptr, &err);
err_ret(err);
ver = 3;
upgrade = true;
}
if (ver < 4) {
// Strings
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS strings "
"(key TEXT, value TEXT, PRIMARY KEY(key))",
nullptr, nullptr, &err);
err_ret(err);
ver = 4;
upgrade = true;
}
if (ver < 5) {
sqlite3_exec(db, "UPDATE policies SET uid=uid%100000", nullptr, nullptr, &err);
err_ret(err);
/* Skip version 5 */
ver = 6;
upgrade = true;
}
if (ver < 7) {
// Hide list
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS hidelist "
"(process TEXT, PRIMARY KEY(process))",
nullptr, nullptr, &err);
err_ret(err);
ver = 7;
upgrade = true;
}
if (ver < 8) {
sqlite3_exec(db,
"ALTER TABLE hidelist ADD COLUMN package_name TEXT;"
"SELECT process FROM hidelist;"
"UPDATE hidelist SET package_name=process;",
nullptr, nullptr, &err);
err_ret(err);
ver = 8;
upgrade = true;
}
if (upgrade) {
// Set version
char query[32];
sprintf(query, "PRAGMA user_version=%d", ver);
sqlite3_exec(db, query, nullptr, nullptr, &err);
err_ret(err);
}
return nullptr;
}
char *db_exec(const char *sql, int (*cb)(void *, int, char**, char**), void *v) {
char *err;
if (mDB == nullptr) {
err = open_and_init_db(mDB);
db_err_cmd(err,
// Open fails, remove and reconstruct
unlink(MAGISKDB);
err = open_and_init_db(mDB);
err_ret(err);
);
}
if (mDB) {
sqlite3_exec(mDB, sql, cb, v, &err);
return err;
}
return nullptr;
}
static int settings_cb(void *v, int col_num, char **data, char **col_name) {
auto &cfg = *(db_settings *) v;
int value = -1;
const char *key = "";
for (int i = 0; i < col_num; ++i) {
if (strcmp(col_name[i], "key") == 0) {
key = data[i];
} else if (strcmp(col_name[i], "value") == 0) {
value = atoi(data[i]);
}
}
if (key[0] && value >= 0) {
cfg[key] = value;
LOGD("magiskdb: query %s=[%d]\n", key, value);
}
return 0;
}
int get_db_settings(db_settings *dbs, int key) {
char *err;
if (key >= 0) {
char query[128];
sprintf(query, "SELECT key, value FROM settings WHERE key='%s'", DB_SETTING_KEYS[key]);
err = db_exec(query, settings_cb, dbs);
} else {
err = db_exec("SELECT key, value FROM settings", settings_cb, dbs);
}
db_err_cmd(err, return 1);
return 0;
}
static int strings_cb(void *v, int col_num, char **data, char **col_name) {
auto &str = *(db_strings *) v;
const char *key = "", *value = "";
for (int i = 0; i < col_num; ++i) {
if (strcmp(col_name[i], "key") == 0) {
key = data[i];
} else if (strcmp(col_name[i], "value") == 0) {
value = data[i];
}
}
if (key[0] && value[0]) {
strcpy(str[key], value);
LOGD("magiskdb: query %s=[%s]\n", key, value);
}
return 0;
}
int get_db_strings(db_strings *str, int key) {
char *err;
if (key >= 0) {
char query[128];
sprintf(query, "SELECT key, value FROM strings WHERE key='%s'", DB_STRING_KEYS[key]);
err = db_exec(query, strings_cb, str);
} else {
err = db_exec("SELECT key, value FROM strings", strings_cb, str);
}
if (err) {
LOGE("sqlite3_exec: %s\n", err);
sqlite3_free(err);
return 1;
}
return 0;
}
static int policy_cb(void *v, int col_num, char **data, char **col_name) {
auto su = (su_access *) v;
for (int i = 0; i < col_num; i++) {
if (strcmp(col_name[i], "policy") == 0)
su->policy = (policy_t) atoi(data[i]);
else if (strcmp(col_name[i], "logging") == 0)
su->log = atoi(data[i]);
else if (strcmp(col_name[i], "notification") == 0)
su->notify = atoi(data[i]);
}
LOGD("magiskdb: query policy=[%d] log=[%d] notify=[%d]\n", su->policy, su->log, su->notify);
return 0;
}
int get_uid_policy(int uid, struct su_access *su) {
char query[256], *err;
sprintf(query, "SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr));
err = db_exec(query, policy_cb, su);
db_err_cmd(err, return 1);
return 0;
}
int validate_manager(char *alt_pkg, int userid, struct stat *st) {
if (st == nullptr) {
struct stat stat;
st = &stat;
}
// Prefer DE storage
const char *base = access("/data/user_de", F_OK) == 0 ? "/data/user_de" : "/data/user";
char app_path[128];
sprintf(app_path, "%s/%d/%s", base, userid, alt_pkg[0] ? alt_pkg : "xxx");
if (stat(app_path, st)) {
// Check the official package name
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, base, userid);
if (stat(app_path, st)) {
LOGE("su: cannot find manager");
memset(st, 0, sizeof(*st));
alt_pkg[0] = '\0';
return 1;
} else {
// Switch to official package if exists
strcpy(alt_pkg, JAVA_PACKAGE_NAME);
}
}
return 0;
}
static int print_cb(void *v, int col_num, char **data, char **col_name) {
FILE *out = (FILE *) v;
for (int i = 0; i < col_num; ++i) {
if (i) fprintf(out, "|");
fprintf(out, "%s=%s", col_name[i], data[i]);
}
fprintf(out, "\n");
return 0;
}
void exec_sql(int client) {
char *sql = read_string(client);
FILE *out = fdopen(recv_fd(client), "a");
char *err = db_exec(sql, print_cb, out);
free(sql);
fclose(out);
db_err_cmd(err,
write_int(client, 1);
return;
);
write_int(client, 0);
}