Skip to content

Commit

Permalink
Merge branch 'tiann:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
whyakari authored Oct 11, 2023
2 parents 3da7b12 + 8828939 commit 2712e49
Show file tree
Hide file tree
Showing 19 changed files with 405 additions and 392 deletions.
4 changes: 2 additions & 2 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ KSU_EXPECTED_SIZE := 0x033b
endif

ifndef KSU_EXPECTED_HASH
KSU_EXPECTED_HASH := 0xb0b91415
KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6
endif

$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))

ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=$(KSU_EXPECTED_HASH)
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-declaration-after-statement
199 changes: 141 additions & 58 deletions kernel/apk_sign.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,133 @@
#include "linux/err.h"
#include "linux/fs.h"
#include "linux/gfp.h"
#include "linux/kernel.h"
#include "linux/moduleparam.h"

#include "apk_sign.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#include "crypto/hash.h"
#include "linux/slab.h"
#include "linux/version.h"

static __always_inline int
check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
#include "crypto/sha2.h"
#else
#include "crypto/sha.h"
#endif

struct sdesc {
struct shash_desc shash;
char ctx[];
};

static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
struct sdesc *sdesc;
int size;

size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
return sdesc;
}

static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
unsigned int datalen, unsigned char *digest)
{
struct sdesc *sdesc;
int ret;

sdesc = init_sdesc(alg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc sdesc\n");
return PTR_ERR(sdesc);
}

ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
}

static int ksu_sha256(const unsigned char *data, unsigned int datalen,
unsigned char *digest)
{
struct crypto_shash *alg;
char *hash_alg_name = "sha256";
int ret;

alg = crypto_alloc_shash(hash_alg_name, 0, 0);
if (IS_ERR(alg)) {
pr_info("can't alloc alg %s\n", hash_alg_name);
return PTR_ERR(alg);
}
ret = calc_hash(alg, data, datalen, digest);
crypto_free_shash(alg);
return ret;
}

static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
unsigned expected_size, const char* expected_sha256)
{
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length

*offset += 0x4 * 3;

ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length

*pos += *size4;
*offset += 0x4 + *size4;

ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
*offset += 0x4 * 2;

if (*size4 == expected_size) {
*offset += *size4;

#define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH];
if (*size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
ksu_kernel_read_compat(fp, cert, *size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
pr_info("sha256 error\n");
return false;
}

char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';

bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s\n", hash_str, expected_sha256);
if (strcmp(expected_sha256, hash_str) == 0) {
return true;
}
}
return false;
}

static __always_inline bool
check_v2_signature(char *path, unsigned expected_size, const char *expected_sha256)
{
unsigned char buffer[0x11] = { 0 };
u32 size4;
u64 size8, size_of_block;

loff_t pos;

int sign = -1;
bool v2_signing_valid = false;
bool v3_signing_exist = false;
bool v3_1_signing_exist = false;

int i;
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
if (IS_ERR(fp)) {
Expand All @@ -25,7 +138,6 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
// disable inotify for this file
fp->f_mode |= FMODE_NONOTIFY;

sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (i = 0;; ++i) {
unsigned short n;
Expand Down Expand Up @@ -64,73 +176,42 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
for (;;) {
uint32_t id;
uint32_t offset;
ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length
ksu_kernel_read_compat(fp, &size8, 0x8,
&pos); // sequence length
if (size8 == size_of_block) {
break;
}
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
offset = 4;
pr_info("id: 0x%08x\n", id);
if ((id ^ 0xdeadbeefu) == 0xafa439f5u ||
(id ^ 0xdeadbeefu) == 0x2efed62f) {
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // signer-sequence length
ksu_kernel_read_compat(fp, &size4, 0x4, &pos); // signer length
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // signed data length
offset += 0x4 * 3;

ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // digests-sequence length
pos += size4;
offset += 0x4 + size4;

ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // certificates length
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // certificate length
offset += 0x4 * 2;
#if 0
int hash = 1;
signed char c;
for (i = 0; i < size4; ++i) {
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
#else
if (size4 == expected_size) {
int hash = 1;
signed char c;
for (i = 0; i < size4; ++i) {
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
if ((((unsigned)hash) ^ 0x14131211u) ==
expected_hash) {
sign = 0;
break;
}
}
// don't try again.
break;
#endif
if (id == 0x7109871au) {
v2_signing_valid = check_block(fp, &size4, &pos, &offset,
expected_size, expected_sha256);
} else if (id == 0xf05368c0u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
v3_signing_exist = true;
} else if (id == 0x1b93ad61u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
v3_1_signing_exist = true;
}
pos += (size8 - offset);
}

clean:
filp_close(fp, 0);

return sign;
if (v3_signing_exist || v3_1_signing_exist) {
pr_err("Unexpected v3 signature scheme found!\n");
return false;
}

return v2_signing_valid;
}

#ifdef CONFIG_KSU_DEBUG

unsigned ksu_expected_size = EXPECTED_SIZE;
unsigned ksu_expected_hash = EXPECTED_HASH;
const char *ksu_expected_hash = EXPECTED_HASH;

#include "manager.h"

Expand All @@ -144,9 +225,10 @@ static int set_expected_size(const char *val, const struct kernel_param *kp)

static int set_expected_hash(const char *val, const struct kernel_param *kp)
{
int rv = param_set_uint(val, kp);
pr_info("set_expected_hash: %s\n", val);
int rv = param_set_charp(val, kp);
ksu_invalidate_manager_uid();
pr_info("ksu_expected_hash set to %x\n", ksu_expected_hash);
pr_info("ksu_expected_hash set to %s\n", ksu_expected_hash);
return rv;
}

Expand All @@ -157,22 +239,23 @@ static struct kernel_param_ops expected_size_ops = {

static struct kernel_param_ops expected_hash_ops = {
.set = set_expected_hash,
.get = param_get_uint,
.get = param_get_charp,
.free = param_free_charp,
};

module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size,
S_IRUSR | S_IWUSR);
module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash,
S_IRUSR | S_IWUSR);

int is_manager_apk(char *path)
bool is_manager_apk(char *path)
{
return check_v2_signature(path, ksu_expected_size, ksu_expected_hash);
}

#else

int is_manager_apk(char *path)
bool is_manager_apk(char *path)
{
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
}
Expand Down
5 changes: 3 additions & 2 deletions kernel/apk_sign.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#ifndef __KSU_H_APK_V2_SIGN
#define __KSU_H_APK_V2_SIGN

// return 0 if signature match
int is_manager_apk(char *path);
#include "linux/types.h"

bool is_manager_apk(char *path);

#endif
2 changes: 1 addition & 1 deletion kernel/manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ bool become_manager(char *pkg)
pr_info("invalid pkg: %s\n", pkg);
continue;
}
if (is_manager_apk(cwd) == 0) {
if (is_manager_apk(cwd)) {
// check passed
uid_t uid = current_uid().val;
pr_info("manager uid: %d\n", uid);
Expand Down
Loading

0 comments on commit 2712e49

Please sign in to comment.