From 348bc1d0fc950f139dd2cd078ab9a101060427b4 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 8 Sep 2016 20:59:48 +0800 Subject: [PATCH] Add extract, repack, hexpatch, sepolicy-injection --- .gitignore | 2 + .gitmodules | 3 + jni/Android.mk | 26 ++ jni/Application.mk | 2 + jni/bootimgtools.c | 45 +++ jni/bootimgtools.h | 90 +++++ jni/extract.c | 141 ++++++++ jni/hexpatch.c | 66 ++++ jni/repack.c | 138 ++++++++ jni/sepolicy-inject.c | 748 ++++++++++++++++++++++++++++++++++++++++++ selinux | 1 + 11 files changed, 1262 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 jni/Android.mk create mode 100644 jni/Application.mk create mode 100644 jni/bootimgtools.c create mode 100644 jni/bootimgtools.h create mode 100644 jni/extract.c create mode 100644 jni/hexpatch.c create mode 100644 jni/repack.c create mode 100644 jni/sepolicy-inject.c create mode 160000 selinux diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ee40a85d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +obj +libs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..adfa0971c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "selinux"] + path = selinux + url = https://github.com/topjohnwu/selinux diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 000000000..003f440ab --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1,26 @@ +my_path := $(call my-dir) + +LOCAL_PATH := $(my_path) + +include $(CLEAR_VARS) +LOCAL_MODULE := bootimgtools +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_LDFLAGS := -static +LOCAL_STATIC_LIBRARIES := libc libcutils +LOCAL_SRC_FILES := bootimgtools.c extract.c repack.c hexpatch.c +LOCAL_CFLAGS += -std=gnu11 +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := sepolicy-inject +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_LDFLAGS := -static +LOCAL_STATIC_LIBRARIES := libc libcutils libsepol +LOCAL_SRC_FILES := sepolicy-inject.c +LOCAL_C_INCLUDES := selinux/libsepol/include/ +LOCAL_CFLAGS += -std=gnu11 +include $(BUILD_EXECUTABLE) + +include selinux/libsepol/Android.mk diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 000000000..cdd5b58c5 --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1,2 @@ +APP_ABI := x86 armeabi +APP_PIE = true diff --git a/jni/bootimgtools.c b/jni/bootimgtools.c new file mode 100644 index 000000000..738a9374d --- /dev/null +++ b/jni/bootimgtools.c @@ -0,0 +1,45 @@ +#include +#include + +#include "bootimgtools.h" + +/******************** + Patch Boot Image +*********************/ + +int usage(char *arg0) { + fprintf(stderr, "Boot Image Unpack/Repack Tool\n"); + fprintf(stderr, "%s --extract \n", arg0); + fprintf(stderr, " Unpack into current directory\n\n"); + fprintf(stderr, "%s --repack \n", arg0); + fprintf(stderr, " Repack kernel, dt, ramdisk... from current directory to new-image.img\n is the image you've just unpacked\n\n"); + fprintf(stderr, "%s --hexpatch \n", arg0); + fprintf(stderr, " Search in , and replace with \n\n"); + return 1; +} + +int main(int argc, char *argv[]) +{ + char ch; + struct option long_options[] = { + {"extract", required_argument, NULL, 'e'}, + {"repack", required_argument, NULL, 'r'}, + {"hexpatch", required_argument, NULL, 'p'}, + {NULL, 0, NULL, 0} + }; + while ((ch = getopt_long(argc, argv, "e:r:p:", long_options, NULL)) != -1) { + switch (ch) { + case 'e': + return extract(optarg); + case 'r': + return repack(optarg); + case 'p': + if (argc < 5) return usage(argv[0]); + optind += 2; + return hexpatch(argv[optind - 3], argv[optind - 2], argv[optind - 1]); + default: + return usage(argv[0]); + } + } + return 0; +} \ No newline at end of file diff --git a/jni/bootimgtools.h b/jni/bootimgtools.h new file mode 100644 index 000000000..a1d7e77fa --- /dev/null +++ b/jni/bootimgtools.h @@ -0,0 +1,90 @@ +/* tools/mkbootimg/bootimg.h +** +** Copyright 2007, The Android Open Source 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 + +#ifndef _BOOT_IMAGE_H_ +#define _BOOT_IMAGE_H_ + +typedef struct boot_img_hdr boot_img_hdr; + +#define BOOT_MAGIC "ANDROID!" +#define BOOT_MAGIC_SIZE 8 +#define BOOT_NAME_SIZE 16 +#define BOOT_ARGS_SIZE 512 +#define BOOT_EXTRA_ARGS_SIZE 1024 + +struct boot_img_hdr +{ + uint8_t magic[BOOT_MAGIC_SIZE]; + + uint32_t kernel_size; /* size in bytes */ + uint32_t kernel_addr; /* physical load addr */ + + uint32_t ramdisk_size; /* size in bytes */ + uint32_t ramdisk_addr; /* physical load addr */ + + uint32_t second_size; /* size in bytes */ + uint32_t second_addr; /* physical load addr */ + + uint32_t tags_addr; /* physical addr for kernel tags */ + uint32_t page_size; /* flash page size we assume */ + uint32_t unused[2]; /* future expansion: should be 0 */ + + uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */ + + uint8_t cmdline[BOOT_ARGS_SIZE]; + + uint32_t id[8]; /* timestamp / checksum / sha1 / etc */ + + /* Supplemental command line data; kept here to maintain + * binary compatibility with older versions of mkbootimg */ + uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; +} __attribute__((packed)); + +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + +int extract(char *image); +int repack(char *image); +int hexpatch(char *image, char *from, char *to); + +#endif diff --git a/jni/extract.c b/jni/extract.c new file mode 100644 index 000000000..393e2140c --- /dev/null +++ b/jni/extract.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootimgtools.h" + +void dump(uint8_t *ptr, size_t size, char* filename) { + unlink(filename); + int ofd = open(filename, O_WRONLY|O_CREAT, 0644); + assert(ofd >= 0); + int ret = write(ofd, ptr, size); + assert(ret == size); + close(ofd); +} + +//TODO: Search for other header types +void dump_ramdisk(uint8_t *ptr, size_t size) { + //GZip header + if(memcmp(ptr, "\x1f\x8b\x08\x00", 4) == 0) { + dump(ptr, size, "ramdisk.gz"); + //MTK header + } else if(memcmp(ptr, "\x88\x16\x88\x58", 4) == 0) { + dump(ptr, 0, "ramdisk-mtk"); //Create an mtk flag + dump_ramdisk(ptr+512, size-512); + } else { + //Since our first aim is to extract/repack ramdisk + //Stop if we can't find it + //Still dump it for debug purposes + dump(ptr, size, "ramdisk"); + + fprintf(stderr, "Unknown ramdisk type\n"); + abort(); + } +} + +void search_security_hdr(uint8_t *buf, size_t size) { + if(memcmp(buf, "CHROMEOS", 8) == 0) { + dump(buf, 0, "chromeos"); + return; + } +} + +int search_security(uint8_t *buf, size_t size, int pos) { + //Rockchip signature + if(memcmp(buf+1024, "SIGN", 4) == 0) { + //Rockchip signature AT LEAST means the bootloader will check the crc + dump(buf, 0, "rkcrc"); //Create an flag to tell it + + //And it's possible there is a security too + return 1; + } + + //If we didn't parse the whole file, it is highly likely there is a boot signature + if(pos < size) { + return 1; + } + + return 0; +} + +/* + * TODO: + * - At the moment we dump kernel + ramdisk + second + DT, it's likely we only want ramdisk + * - Error-handling via assert() is perhaps not the best + */ +int extract(char *image) { + + int fd = open(image, O_RDONLY); + off_t size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + uint8_t *orig = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + uint8_t *base = orig; + assert(base); + + search_security_hdr(base, size); + + //We're searching for the header in the whole file, we could stop earlier. + //At least HTC and nVidia have a signature header + while(base<(orig+size)) { + if(memcmp(base, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) + break; + //We're searching every 256bytes, is it ok? + base += 256; + } + assert(base < (orig+size)); + + struct boot_img_hdr *hdr = (struct boot_img_hdr*) base; + assert( + hdr->page_size == 2048 || + hdr->page_size == 4096 || + hdr->page_size == 16384 + ); + + long pos = hdr->page_size; + dump(base+pos, hdr->kernel_size, "kernel"); + pos += hdr->kernel_size + hdr->page_size-1; + pos &= ~(hdr->page_size-1L); + + dump_ramdisk(base+pos, hdr->ramdisk_size); + pos += hdr->ramdisk_size + hdr->page_size-1; + pos &= ~(hdr->page_size-1L); + + if(hdr->second_size) { + assert( (pos+hdr->second_size) <= size); + dump(base+pos, hdr->second_size, "second"); + pos += hdr->second_size + hdr->page_size-1; + pos &= ~(hdr->page_size-1L); + } + + //This is non-standard, so we triple check + if( hdr->unused[0] && + pos < size && + (pos+hdr->unused[0]) <= size) { + + if(memcmp(base+pos, "QCDT", 4) == 0 || + memcmp(base+pos, "SPRD", 4) == 0 || + memcmp(base+pos, "DTBH", 4) == 0 + ) { + dump(base+pos, hdr->unused[0], "dt"); + pos += hdr->unused[0] + hdr->page_size-1; + pos &= ~(hdr->page_size-1L); + } + } + + //If we think we find some security-related infos in the boot.img + //create a "secure" flag to warn the user it is dangerous + if(search_security(base, size, pos)) { + dump(base, 0, "secure"); + } + + munmap(orig, size); + close(fd); + return 0; +} diff --git a/jni/hexpatch.c b/jni/hexpatch.c new file mode 100644 index 000000000..469538478 --- /dev/null +++ b/jni/hexpatch.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootimgtools.h" + +int hex2int(char c) { + int first = c / 16 - 3; + int second = c % 16; + int result = first * 10 + second; + if(result > 9) result--; + return result; +} + +int hex2ascii(char c, char d) { + int high = hex2int(c) * 16; + int low = hex2int(d); + return high+low; +} + +void hexstr2str(char *hex, char *str) { + char buf = 0; + for(int i = 0, length = strlen(hex); i < length; ++i){ + if(i % 2){ + str[i / 2] = hex2ascii(buf, hex[i]); + } else{ + buf = hex[i]; + } + } +} + +int hexpatch(char * image, char *from, char *to) { + int fd = open(image, O_RDWR), patternsize = strlen(from) / 2, patchsize = strlen(to) / 2; + off_t filesize = lseek(fd, 0, SEEK_END); + char *file, *pattern, *patch, *start; + file = malloc(sizeof (char) * filesize); + pattern = malloc(sizeof (char) * patternsize); + patch = malloc(sizeof (char) * patchsize); + lseek(fd, 0, SEEK_SET); + read(fd, file, filesize); + hexstr2str(from, pattern); + hexstr2str(to, patch); + for (off_t i = 0; i < filesize;) { + int j; + for (j = 0; j < patternsize; ++j) { + if(file[i + j] != pattern[j]) break; + } + if (j == patternsize) { + fprintf(stderr, "Pattern %s found!\nPatching to %s\n", from, to); + lseek(fd, i, SEEK_SET); + write(fd, patch, patchsize); + } + if(j == 0) j = 1; + i += j; + } + free(file); + free(pattern); + free(patch); + close(fd); + return 0; +} \ No newline at end of file diff --git a/jni/repack.c b/jni/repack.c new file mode 100644 index 000000000..ab6febbc1 --- /dev/null +++ b/jni/repack.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootimgtools.h" + +off_t file_size(char *filename) { + struct stat st; + if(stat(filename, &st)) + exit(1); + return st.st_size; +} + +int append_file(int ofd, char *filename, off_t pos) { + lseek(ofd, pos, SEEK_SET); + int fd = open(filename, O_RDONLY); + int size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + sendfile(ofd, fd, NULL, size); + close(fd); + return size; +} + +int append_ramdisk(int ofd, off_t pos) { + if(access("ramdisk-mtk", R_OK) == 0) { + char buf[512]; + off_t size = file_size("ramdisk.gz"); + memcpy(buf, "\x88\x16\x88\x58", 4); + uint32_t v = size; + memcpy(buf+4, &v, sizeof(v)); //Should convert to LE + + //TODO: RECOVERY OR ROOTFS? + char str[32]; + memset(str, 0, sizeof(str)); + strcpy(str, "ROOTFS"); + memcpy(buf+8, str, sizeof(str)); + + memset(buf+8+sizeof(str), 0xff, 512-8-sizeof(str)); + + pwrite(ofd, buf, sizeof(buf), pos); + + return append_file(ofd, "ramdisk.gz", pos + 512) + 512; + } else if(access("ramdisk.gz", R_OK) == 0) { + return append_file(ofd, "ramdisk.gz", pos); + } else { + return append_file(ofd, "ramdisk", pos); + } +} + +void post_process(struct boot_img_hdr *hdr, int ofd, int pos) { + if(access("rkcrc", R_OK) == 0) { + fprintf(stderr, "Rockchip CRCs not supported yet\n"); + exit(1); + } + //Round up the file size + ftruncate(ofd, pos); +} + +int repack(char *image) { + + //TODO: Merge with extract.c? + //{ + int ifd = open(image, O_RDONLY); + off_t isize = lseek(ifd, 0, SEEK_END); + lseek(ifd, 0, SEEK_SET); + uint8_t *iorig = mmap(NULL, isize, PROT_READ, MAP_SHARED, ifd, 0); + uint8_t *ibase = iorig; + assert(ibase); + + while(ibase<(iorig+isize)) { + if(memcmp(ibase, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) + break; + ibase += 256; + } + assert(ibase < (iorig+isize)); + //} + // + struct boot_img_hdr *ihdr = (struct boot_img_hdr*) ibase; + assert( + ihdr->page_size == 2048 || + ihdr->page_size == 4096 || + ihdr->page_size == 16384 + ); + + unlink("new-boot.img"); + int ofd = open("new-boot.img", O_RDWR|O_CREAT, 0644); + ftruncate(ofd, ihdr->page_size); + //Write back original header, we'll change it later + write(ofd, ihdr, sizeof(*ihdr)); + + struct boot_img_hdr *hdr = mmap(NULL, sizeof(*ihdr), PROT_READ|PROT_WRITE, MAP_SHARED, ofd, 0); + //First set everything to zero, so we know where we are at. + hdr->kernel_size = 0; + hdr->ramdisk_size = 0; + hdr->second_size = 0; + hdr->unused[0] = 0; + memset(hdr->id, 0, sizeof(hdr->id)); //Setting id to 0 might be wrong? + + int pos = hdr->page_size; + int size = 0; + + size = append_file(ofd, "kernel", pos); + pos += size + hdr->page_size - 1; + pos &= ~(hdr->page_size-1); + hdr->kernel_size = size; + + size = append_ramdisk(ofd, pos); + pos += size + hdr->page_size - 1; + pos &= ~(hdr->page_size-1); + hdr->ramdisk_size = size; + + if(access("second", R_OK) == 0) { + size = append_file(ofd, "second", pos); + pos += size + hdr->page_size - 1; + pos &= ~(hdr->page_size-1); + hdr->second_size = size; + } + + if(access("dt", R_OK) == 0) { + size = append_file(ofd, "dt", pos); + pos += size + hdr->page_size - 1; + pos &= ~(hdr->page_size-1); + hdr->unused[0] = size; + } + + post_process(hdr, ofd, pos); + munmap(hdr, sizeof(*ihdr)); + close(ofd); + + return 0; +} diff --git a/jni/sepolicy-inject.c b/jni/sepolicy-inject.c new file mode 100644 index 000000000..053d3a012 --- /dev/null +++ b/jni/sepolicy-inject.c @@ -0,0 +1,748 @@ +/* + * This was derived from public domain works with updates to + * work with more modern SELinux libraries. + * + * It is released into the public domain. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void usage(char *arg0) { + fprintf(stderr, "%s -s -t -c -p -P \n", arg0); + fprintf(stderr, "\tInject a rule\n\n"); + fprintf(stderr, "%s -s -a -P \n", arg0); + fprintf(stderr, "\tAdd a type_attribute to a domain\n\n"); + fprintf(stderr, "%s -Z -P \n", arg0); + fprintf(stderr, "\tInject a permissive domain\n\n"); + fprintf(stderr, "%s -z -P \n", arg0); + fprintf(stderr, "\tInject a non-permissive domain\n\n"); + fprintf(stderr, "%s -e -s -P \n", arg0); + fprintf(stderr, "\tCheck if a SELinux type exists\n\n"); + fprintf(stderr, "%s -e -c -P \n", arg0); + fprintf(stderr, "\tCheck if a SELinux class exists\n\n"); + fprintf(stderr, "All options can add -o to output to another file\n"); + exit(1); +} + +void *cmalloc(size_t s) { + void *t = malloc(s); + if (t == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + return t; +} + +int get_attr(char *type, int value, policydb_t *policy) { + type_datum_t *attr = hashtab_search(policy->p_types.table, type); + if (!attr) + exit(1); + + if (attr->flavor != TYPE_ATTRIB) + exit(1); + + return !! ebitmap_get_bit(&policy->attr_type_map[attr->s.value-1], value-1); + //return !! ebitmap_get_bit(&policy->type_attr_map[value-1], attr->s.value-1); +} + +int get_attr_id(char *type, policydb_t *policy) { + type_datum_t *attr = hashtab_search(policy->p_types.table, type); + if (!attr) + exit(1); + + if (attr->flavor != TYPE_ATTRIB) + exit(1); + + return attr->s.value; +} + +int set_attr(char *type, int value, policydb_t *policy) { + type_datum_t *attr = hashtab_search(policy->p_types.table, type); + if (!attr) + exit(1); + + if (attr->flavor != TYPE_ATTRIB) + exit(1); + + if(ebitmap_set_bit(&policy->type_attr_map[value-1], attr->s.value-1, 1)) + exit(1); + if(ebitmap_set_bit(&policy->attr_type_map[attr->s.value-1], value-1, 1)) + exit(1); + + return 0; +} + +void create_domain(char *d, policydb_t *policy) { + symtab_datum_t *src = hashtab_search(policy->p_types.table, d); + if(src) + return; + + type_datum_t *typdatum = (type_datum_t *) malloc(sizeof(type_datum_t)); + type_datum_init(typdatum); + typdatum->primary = 1; + typdatum->flavor = TYPE_TYPE; + + uint32_t value = 0; + int r = symtab_insert(policy, SYM_TYPES, strdup(d), typdatum, SCOPE_DECL, 1, &value); + typdatum->s.value = value; + + fprintf(stderr, "source type %s does not exist: %d,%d\n", d, r, value); + if (ebitmap_set_bit(&policy->global->branch_list->declared.scope[SYM_TYPES], value - 1, 1)) { + exit(1); + } + + policy->type_attr_map = realloc(policy->type_attr_map, sizeof(ebitmap_t)*policy->p_types.nprim); + policy->attr_type_map = realloc(policy->attr_type_map, sizeof(ebitmap_t)*policy->p_types.nprim); + ebitmap_init(&policy->type_attr_map[value-1]); + ebitmap_init(&policy->attr_type_map[value-1]); + ebitmap_set_bit(&policy->type_attr_map[value-1], value-1, 1); + + //Add the domain to all roles + for(unsigned i=0; ip_roles.nprim; ++i) { + //Not sure all those three calls are needed + ebitmap_set_bit(&policy->role_val_to_struct[i]->types.negset, value-1, 0); + ebitmap_set_bit(&policy->role_val_to_struct[i]->types.types, value-1, 1); + type_set_expand(&policy->role_val_to_struct[i]->types, &policy->role_val_to_struct[i]->cache, policy, 0); + } + + + src = hashtab_search(policy->p_types.table, d); + if(!src) + exit(1); + + extern int policydb_index_decls(policydb_t * p); + if(policydb_index_decls(policy)) + exit(1); + + if(policydb_index_classes(policy)) + exit(1); + + if(policydb_index_others(NULL, policy, 1)) + exit(1); + + set_attr("domain", value, policy); +} + +int add_irule(int s, int t, int c, int p, int effect, int not, policydb_t* policy) { + avtab_datum_t *av; + avtab_key_t key; + + key.source_type = s; + key.target_type = t; + key.target_class = c; + key.specified = effect; + av = avtab_search(&policy->te_avtab, &key); + + if (av == NULL) { + av = cmalloc(sizeof(*av)); + av->data |= 1U << (p - 1); + int ret = avtab_insert(&policy->te_avtab, &key, av); + if (ret) { + fprintf(stderr, "Error inserting into avtab\n"); + return 1; + } + } + + if(not) + av->data &= ~(1U << (p - 1)); + else + av->data |= 1U << (p - 1); + return 0; +} + +int add_rule(char *s, char *t, char *c, char *p, int effect, int not, policydb_t *policy) { + type_datum_t *src, *tgt; + class_datum_t *cls; + perm_datum_t *perm; + + src = hashtab_search(policy->p_types.table, s); + if (src == NULL) { + fprintf(stderr, "source type %s does not exist\n", s); + return 1; + } + tgt = hashtab_search(policy->p_types.table, t); + if (tgt == NULL) { + fprintf(stderr, "target type %s does not exist\n", t); + return 1; + } + cls = hashtab_search(policy->p_classes.table, c); + if (cls == NULL) { + fprintf(stderr, "class %s does not exist\n", c); + return 1; + } + perm = hashtab_search(cls->permissions.table, p); + if (perm == NULL) { + if (cls->comdatum == NULL) { + fprintf(stderr, "perm %s does not exist in class %s\n", p, c); + return 1; + } + perm = hashtab_search(cls->comdatum->permissions.table, p); + if (perm == NULL) { + fprintf(stderr, "perm %s does not exist in class %s\n", p, c); + return 1; + } + } + + return add_irule(src->s.value, tgt->s.value, cls->s.value, perm->s.value, effect, not, policy); +} + +int add_typerule(char *s, char *targetAttribute, char **minusses, char *c, char *p, int effect, int not, policydb_t *policy) { + type_datum_t *src, *tgt; + class_datum_t *cls; + perm_datum_t *perm; + + //64(0kB) should be enough for everyone, right? + int m[64] = { -1 }; + + src = hashtab_search(policy->p_types.table, s); + if (src == NULL) { + fprintf(stderr, "source type %s does not exist\n", s); + return 1; + } + + tgt = hashtab_search(policy->p_types.table, targetAttribute); + if (tgt == NULL) { + fprintf(stderr, "target type %s does not exist\n", targetAttribute); + return 1; + } + if(tgt->flavor != TYPE_ATTRIB) + exit(1); + + for(int i=0; minusses && minusses[i]; ++i) { + type_datum_t *obj; + obj = hashtab_search(policy->p_types.table, minusses[i]); + if (obj == NULL) { + fprintf(stderr, "minus type %s does not exist\n", minusses[i]); + return 1; + } + m[i] = obj->s.value-1; + m[i+1] = -1; + } + + cls = hashtab_search(policy->p_classes.table, c); + if (cls == NULL) { + fprintf(stderr, "class %s does not exist\n", c); + return 1; + } + + perm = hashtab_search(cls->permissions.table, p); + if (perm == NULL) { + if (cls->comdatum == NULL) { + fprintf(stderr, "perm %s does not exist in class %s\n", p, c); + return 1; + } + perm = hashtab_search(cls->comdatum->permissions.table, p); + if (perm == NULL) { + fprintf(stderr, "perm %s does not exist in class %s\n", p, c); + return 1; + } + } + + ebitmap_node_t *node; + int i; + + int ret = 0; + + ebitmap_for_each_bit(&policy->attr_type_map[tgt->s.value-1], node, i) { + if(ebitmap_node_get_bit(node, i)) { + int found = 0; + for(int j=0; m[j] != -1; ++j) { + if(i == m[j]) + found = 1; + } + + if(!found) + ret |= add_irule(src->s.value, i+1, cls->s.value, perm->s.value, effect, not, policy); + } + } + return ret; +} + +int add_transition(char *srcS, char *origS, char *tgtS, char *c, policydb_t *policy) { + type_datum_t *src, *tgt, *orig; + class_datum_t *cls; + + avtab_datum_t *av; + avtab_key_t key; + + src = hashtab_search(policy->p_types.table, srcS); + if (src == NULL) { + fprintf(stderr, "source type %s does not exist\n", srcS); + return 1; + } + tgt = hashtab_search(policy->p_types.table, tgtS); + if (tgt == NULL) { + fprintf(stderr, "target type %s does not exist\n", tgtS); + return 1; + } + cls = hashtab_search(policy->p_classes.table, c); + if (cls == NULL) { + fprintf(stderr, "class %s does not exist\n", c); + return 1; + } + orig = hashtab_search(policy->p_types.table, origS); + if (cls == NULL) { + fprintf(stderr, "class %s does not exist\n", origS); + return 1; + } + + key.source_type = src->s.value; + key.target_type = orig->s.value; + key.target_class = cls->s.value; + key.specified = AVTAB_TRANSITION; + av = avtab_search(&policy->te_avtab, &key); + + if (av == NULL) { + av = cmalloc(sizeof(*av)); + av->data = tgt->s.value; + int ret = avtab_insert(&policy->te_avtab, &key, av); + if (ret) { + fprintf(stderr, "Error inserting into avtab\n"); + return 1; + } + } else { + fprintf(stderr, "Warning, rule already defined! Won't override.\n"); + fprintf(stderr, "Previous value = %d, wanted value = %d\n", av->data, tgt->s.value); + } + + return 0; +} + +int add_file_transition(char *srcS, char *origS, char *tgtS, char *c, char* filename, policydb_t *policy) { + type_datum_t *src, *tgt, *orig; + class_datum_t *cls; + + src = hashtab_search(policy->p_types.table, srcS); + if (src == NULL) { + fprintf(stderr, "source type %s does not exist\n", srcS); + return 1; + } + tgt = hashtab_search(policy->p_types.table, tgtS); + if (tgt == NULL) { + fprintf(stderr, "target type %s does not exist\n", tgtS); + return 1; + } + cls = hashtab_search(policy->p_classes.table, c); + if (cls == NULL) { + fprintf(stderr, "class %s does not exist\n", c); + return 1; + } + orig = hashtab_search(policy->p_types.table, origS); + if (cls == NULL) { + fprintf(stderr, "class %s does not exist\n", origS); + return 1; + } + + filename_trans_t *new_transition = cmalloc(sizeof(*new_transition)); + new_transition->stype = src->s.value; + new_transition->ttype = orig->s.value; + new_transition->tclass = cls->s.value; + new_transition->otype = tgt->s.value; + new_transition->name = strdup(filename); + new_transition->next = policy->filename_trans; + + policy->filename_trans = new_transition; + + return 0; +} + +int add_type(char *domainS, char *typeS, policydb_t *policy) { + type_datum_t *domain; + + domain = hashtab_search(policy->p_types.table, domainS); + if (domain == NULL) { + fprintf(stderr, "source type %s does not exist\n", domainS); + return 1; + } + + set_attr(typeS, domain->s.value, policy); + + int typeId = get_attr_id(typeS, policy); + //Now let's update all constraints! + //(kernel doesn't support (yet?) type_names rules) + for(int i=0; ip_classes.nprim; ++i) { + class_datum_t *cl = policy->class_val_to_struct[i]; + for(constraint_node_t *n = cl->constraints; n ; n=n->next) { + for(constraint_expr_t *e = n->expr; e; e=e->next) { + if(e->expr_type == CEXPR_NAMES) { + if(ebitmap_get_bit(&e->type_names->types, typeId-1)) { + ebitmap_set_bit(&e->names, domain->s.value-1, 1); + } + } + } + } + } + return 0; +} + +int load_policy(char *filename, policydb_t *policydb, struct policy_file *pf) { + int fd; + struct stat sb; + void *map; + int ret; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Can't open '%s': %s\n", + filename, strerror(errno)); + return 1; + } + if (fstat(fd, &sb) < 0) { + fprintf(stderr, "Can't stat '%s': %s\n", + filename, strerror(errno)); + return 1; + } + map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + if (map == MAP_FAILED) { + fprintf(stderr, "Can't mmap '%s': %s\n", + filename, strerror(errno)); + return 1; + } + + policy_file_init(pf); + pf->type = PF_USE_MEMORY; + pf->data = map; + pf->len = sb.st_size; + if (policydb_init(policydb)) { + fprintf(stderr, "policydb_init: Out of memory!\n"); + return 1; + } + ret = policydb_read(policydb, pf, 1); + if (ret) { + fprintf(stderr, "error(s) encountered while parsing configuration\n"); + return 1; + } + + return 0; +} + +int auto_allow(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, policydb_t *policy) { + perm_datum_t *perm; + hashtab_t type_table, class_table, perm_table; + hashtab_ptr_t cur; + + type_table = policy->p_types.table; + class_table = policy->p_classes.table; + + if (src == NULL) { + for (int i = 0; i < type_table->size; ++i) { + cur = type_table->htable[i]; + while (cur != NULL) { + src = cur->datum; + if(auto_allow(src, tgt, cls, policy)) + return 1; + cur = cur->next; + } + } + } else if (tgt == NULL) { + for (int i = 0; i < type_table->size; ++i) { + cur = type_table->htable[i]; + while (cur != NULL) { + tgt = cur->datum; + if(auto_allow(src, tgt, cls, policy)) + return 1; + cur = cur->next; + } + } + } else if (cls == NULL) { + for (int i = 0; i < class_table->size; ++i) { + cur = class_table->htable[i]; + while (cur != NULL) { + cls = cur->datum; + if(auto_allow(src, tgt, cls, policy)) + return 1; + cur = cur->next; + } + } + } else { + perm_table = cls->permissions.table; + for (int i = 0; i < perm_table->size; ++i) { + cur = perm_table->htable[i]; + while (cur != NULL) { + perm = cur->datum; + if(add_irule(src->s.value, tgt->s.value, cls->s.value, perm->s.value, AVTAB_ALLOWED, 0, policy)) + return 1; + cur = cur->next; + } + } + + if (cls->comdatum != NULL) { + perm_table = cls->comdatum->permissions.table; + for (int i = 0; i < perm_table->size; ++i) { + cur = perm_table->htable[i]; + while (cur != NULL) { + perm = cur->datum; + if(add_irule(src->s.value, tgt->s.value, cls->s.value, perm->s.value, AVTAB_ALLOWED, 0, policy)) + return 1; + cur = cur->next; + } + } + } + } + return 0; +} + +int live_patch(policydb_t *policydb) { + char *filename = "/sys/fs/selinux/load"; + int fd, ret; + void *data = NULL; + size_t len; + + policydb_to_image(NULL, policydb, &data, &len); + if (data == NULL) fprintf(stderr, "Error!"); + + // based on libselinux security_load_policy() + fd = open(filename, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Can't open '%s': %s\n", + filename, strerror(errno)); + return 1; + } + ret = write(fd, data, len); + close(fd); + if (ret < 0) { + fprintf(stderr, "Could not write policy to %s\n", + filename); + return 1; + } + return 0; +} + +int main(int argc, char **argv) +{ + char *policy = NULL, *source = NULL, *target = NULL, *class = NULL, *perm = NULL; + char *fcon = NULL, *outfile = NULL, *permissive = NULL, *attr = NULL, *filetrans = NULL; + int exists = 0, not = 0, autoAllow = 0, live = 0; + policydb_t policydb; + struct policy_file pf, outpf; + sidtab_t sidtab; + int ch; + FILE *fp; + int permissive_value = 0, noaudit = 0; + + struct option long_options[] = { + {"attr", required_argument, NULL, 'a'}, + {"exists", no_argument, NULL, 'e'}, + {"source", required_argument, NULL, 's'}, + {"target", required_argument, NULL, 't'}, + {"class", required_argument, NULL, 'c'}, + {"perm", required_argument, NULL, 'p'}, + {"fcon", required_argument, NULL, 'f'}, + {"filetransition", required_argument, NULL, 'g'}, + {"noaudit", no_argument, NULL, 'n'}, + {"policy", required_argument, NULL, 'P'}, + {"output", required_argument, NULL, 'o'}, + {"permissive", required_argument, NULL, 'Z'}, + {"not-permissive", required_argument, NULL, 'z'}, + {"not", no_argument, NULL, 0}, + {"auto", no_argument, NULL, 0}, + {"live", no_argument, NULL, 0}, + {NULL, 0, NULL, 0} + }; + + int option_index = -1; + while ((ch = getopt_long(argc, argv, "a:c:ef:g:s:t:p:P:o:Z:z:n", long_options, &option_index)) != -1) { + switch (ch) { + case 0: + if(strcmp(long_options[option_index].name, "not") == 0) + not = 1; + else if(strcmp(long_options[option_index].name, "auto") == 0) + autoAllow = 1; + else if(strcmp(long_options[option_index].name, "live") == 0) + live = 1; + else + usage(argv[0]); + break; + case 'a': + attr = optarg; + break; + case 'e': + exists = 1; + break; + case 'f': + fcon = optarg; + break; + case 'g': + filetrans = optarg; + break; + case 's': + source = optarg; + break; + case 't': + target = optarg; + break; + case 'c': + class = optarg; + break; + case 'p': + perm = optarg; + break; + case 'P': + policy = optarg; + break; + case 'o': + outfile = optarg; + break; + case 'Z': + permissive = optarg; + permissive_value = 1; + break; + case 'z': + permissive = optarg; + permissive_value = 0; + break; + case 'n': + noaudit = 1; + break; + default: + usage(argv[0]); + } + } + + if ((!source || !target || !class || !perm) && !permissive && !fcon && !attr &&!filetrans && !exists && !auto_allow ) + usage(argv[0]); + + if(!outfile) + outfile = policy; + + if(!policy) + policy = "/sys/fs/selinux/policy"; + + sepol_set_policydb(&policydb); + sepol_set_sidtab(&sidtab); + + if (load_policy(policy, &policydb, &pf)) { + fprintf(stderr, "Could not load policy\n"); + return 1; + } + + if (policydb_load_isids(&policydb, &sidtab)) + return 1; + + if (autoAllow) { + type_datum_t *src = NULL, *tgt = NULL; + class_datum_t *cls = NULL; + if (source) + src = hashtab_search(policydb.p_types.table, source); + if (target) + tgt = hashtab_search(policydb.p_types.table, target); + if (class) + cls = hashtab_search(policydb.p_classes.table, class); + if (auto_allow(src, tgt, cls, &policydb)) + return 1; + } else if (permissive) { + type_datum_t *type; + create_domain(permissive, &policydb); + type = hashtab_search(policydb.p_types.table, permissive); + if (type == NULL) { + fprintf(stderr, "type %s does not exist\n", permissive); + return 1; + } + if (ebitmap_set_bit(&policydb.permissive_map, type->s.value, permissive_value)) { + fprintf(stderr, "Could not set bit in permissive map\n"); + return 1; + } + } else if(exists) { + if(source) { + type_datum_t *tmp = hashtab_search(policydb.p_types.table, source); + if (!tmp) + exit(1); + else + exit(0); + } else if(class) { + class_datum_t *tmp = hashtab_search(policydb.p_classes.table, class); + if(!tmp) + exit(1); + else + exit(0); + } else { + usage(argv[0]); + } + } else if(filetrans) { + if(add_file_transition(source, fcon, target, class, filetrans, &policydb)) + return 1; + } else if(fcon) { + if(add_transition(source, fcon, target, class, &policydb)) + return 1; + } else if(attr) { + if(add_type(source, attr, &policydb)) + return 1; + } else if(noaudit) { + if(add_rule(source, target, class, perm, AVTAB_AUDITDENY, not, &policydb)) + return 1; + } else { + //Add a rule to a whole set of typeattribute, not just a type + if(*target == '=') { + char *saveptr = NULL; + + char *targetAttribute = strtok_r(target, "-", &saveptr); + + char *vals[64]; + int i = 0; + + char *m = NULL; + while( (m = strtok_r(NULL, "-", &saveptr)) != NULL) { + vals[i++] = m; + } + vals[i] = NULL; + + if(add_typerule(source, targetAttribute+1, vals, class, perm, AVTAB_ALLOWED, not, &policydb)) + return 1; + } else { + char *saveptr = NULL; + + char *p = strtok_r(perm, ",", &saveptr); + do { + if (add_rule(source, target, class, p, AVTAB_ALLOWED, not, &policydb)) { + fprintf(stderr, "Could not add rule\n"); + return 1; + } + } while( (p = strtok_r(NULL, ",", &saveptr)) != NULL); + } + } + + if (live) { + if (live_patch(&policydb)) { + fprintf(stderr, "Could not load new policy into kernel\n"); + return 1; + } + } + + if (outfile) { + fp = fopen(outfile, "w"); + if (!fp) { + fprintf(stderr, "Could not open outfile\n"); + return 1; + } + + policy_file_init(&outpf); + outpf.type = PF_USE_STDIO; + outpf.fp = fp; + + if (policydb_write(&policydb, &outpf)) { + fprintf(stderr, "Could not write policy\n"); + return 1; + } + fclose(fp); + } + + policydb_destroy(&policydb); + return 0; +} diff --git a/selinux b/selinux new file mode 160000 index 000000000..df7346cd5 --- /dev/null +++ b/selinux @@ -0,0 +1 @@ +Subproject commit df7346cd5b27f8a66f84669a9ed1d9c272692b17