From 4bcab4f4e6a53f2b7d402092983ee0d71d555259 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sun, 23 Oct 2016 09:02:39 -0400 Subject: appimage type 2 support --- src/firejail/appimage.c | 29 ++++++++- src/firejail/appimage_size.c | 143 +++++++++++++++++++++++++++++++++++++++++++ src/firejail/firejail.h | 4 ++ 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 src/firejail/appimage_size.c diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 375d6be24..3f03c28bf 100644 --- a/src/firejail/appimage.c +++ b/src/firejail/appimage.c @@ -39,7 +39,7 @@ void appimage_set(const char *appimage_path) { assert(appimage_path); assert(devloop == NULL); // don't call this twice! EUID_ASSERT(); - + #ifdef LOOP_CTL_GET_FREE // test for older kernels; this definition is found in /usr/include/linux/loop.h // check appimage_path if (access(appimage_path, R_OK) == -1) { @@ -47,6 +47,12 @@ void appimage_set(const char *appimage_path) { exit(1); } + // get appimage type and ELF size + // a value of 0 means we are dealing with a type1 appimage + long unsigned int size = appimage2_size(appimage_path); + if (arg_debug) + printf("AppImage ELF size %lu\n", size); + // open as user to prevent race condition int ffd = open(appimage_path, O_RDONLY|O_CLOEXEC); if (ffd == -1) { @@ -76,6 +82,15 @@ void appimage_set(const char *appimage_path) { fprintf(stderr, "Error: cannot configure the loopback device\n"); exit(1); } + + if (size) { + struct loop_info64 info; + memset(&info, 0, sizeof(struct loop_info64)); + info.lo_offset = size; + if (ioctl(lfd, LOOP_SET_STATUS64, &info) == -1) + errExit("configure appimage offset"); + } + close(lfd); close(ffd); EUID_USER(); @@ -100,8 +115,16 @@ void appimage_set(const char *appimage_path) { if (asprintf(&mode, "mode=700,uid=%d,gid=%d", getuid(), getgid()) == -1) errExit("asprintf"); EUID_ROOT(); - if (mount(devloop, mntdir, "iso9660",MS_MGC_VAL|MS_RDONLY, mode) < 0) - errExit("mounting appimage"); + + if (size == 0) { + if (mount(devloop, mntdir, "iso9660",MS_MGC_VAL|MS_RDONLY, mode) < 0) + errExit("mounting appimage"); + } + else { + if (mount(devloop, mntdir, "squashfs",MS_MGC_VAL|MS_RDONLY, mode) < 0) + errExit("mounting appimage"); + } + if (arg_debug) printf("appimage mounted on %s\n", mntdir); EUID_USER(); diff --git a/src/firejail/appimage_size.c b/src/firejail/appimage_size.c new file mode 100644 index 000000000..c8b3d28c5 --- /dev/null +++ b/src/firejail/appimage_size.c @@ -0,0 +1,143 @@ +/* +Compile with: +gcc elfsize.c -o elfsize +Example: +ls -l 126584 +Calculation using the values also reported by readelf -h: +Start of section headers e_shoff 124728 +Size of section headers e_shentsize 64 +Number of section headers e_shnum 29 +e_shoff + ( e_shentsize * e_shnum ) = 126584 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef Elf32_Nhdr Elf_Nhdr; + +static Elf64_Ehdr ehdr; +static Elf64_Phdr *phdr; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ELFDATANATIVE ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATANATIVE ELFDATA2MSB +#else +#error "Unknown machine endian" +#endif + +static uint16_t file16_to_cpu(uint16_t val) { + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_16(val); + return val; +} + + +static uint32_t file32_to_cpu(uint32_t val) { + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_32(val); + return val; +} + + +static uint64_t file64_to_cpu(uint64_t val) { + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_64(val); + return val; +} + + +// return 0 if error +static long unsigned int read_elf32(int fd) { + Elf32_Ehdr ehdr32; + ssize_t ret, i; + + ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); + if (ret < 0 || (size_t)ret != sizeof(ehdr)) + return 0; + + ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); + ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); + + return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum)); +} + + +// return 0 if error +static long unsigned int read_elf64(int fd) { + Elf64_Ehdr ehdr64; + ssize_t ret, i; + + ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); + if (ret < 0 || (size_t)ret != sizeof(ehdr)) + return 0; + + ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); + ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); + + return(ehdr.e_shoff + (ehdr.e_shentsize * ehdr.e_shnum)); +} + + +// return 0 if error +// return 0 if this is not an appimgage2 file +long unsigned int appimage2_size(const char *fname) { +/* TODO, FIXME: This assumes that the section header table (SHT) is +the last part of the ELF. This is usually the case but +it could also be that the last section is the last part +of the ELF. This should be checked for. +*/ + ssize_t ret; + int fd; + long unsigned int size = 0; + + fd = open(fname, O_RDONLY); + if (fd < 0) + return 0; + + ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); + if (ret != EI_NIDENT) + goto getout; + + if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && + (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) + goto getout; + + if(ehdr.e_ident[EI_CLASS] == ELFCLASS32) { + size = read_elf32(fd); + } + else if(ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + size = read_elf64(fd); + } + else { + goto getout; + } + if (size == 0) + goto getout; + + + // look for a LZMA header at this location + unsigned char buf[4]; + ret = pread(fd, buf, 4, size); + if (ret != 4) { + size = 0; + goto getout; + } + if (memcmp(buf, "hsqs", 4) != 0) + size = 0; + +getout: + close(fd); + return size; +} + + diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index dafa5919c..9a9bb1ae7 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -681,8 +681,12 @@ void appimage_set(const char *appimage_path); void appimage_clear(void); const char *appimage_getdir(void); +// appimage_size.c +long unsigned int appimage2_size(const char *fname); + // cmdline.c void build_cmdline(char **command_line, char **window_title, int argc, char **argv, int index); + #endif -- cgit v1.2.3-54-g00ecf