Skip to content

Commit

Permalink
Add emulation of the stat family on mknod'd files.
Browse files Browse the repository at this point in the history
  • Loading branch information
remexre committed Feb 6, 2025
1 parent 5f780cb commit b694332
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 23 deletions.
18 changes: 18 additions & 0 deletions src/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ typedef unsigned char byte_t;

#define OFFSETOF_STATX_UID 20
#define OFFSETOF_STATX_GID 24
#define OFFSETOF_STATX_MODE 28
#define OFFSETOF_STATX_INO 32
#define OFFSETOF_STATX_RDEV_MAJOR 128
#define OFFSETOF_STATX_RDEV_MINOR 132
#define OFFSETOF_STATX_DEV_MAJOR 136
#define OFFSETOF_STATX_DEV_MINOR 140

#if !defined(ARCH_X86_64) && !defined(ARCH_ARM_EABI) && !defined(ARCH_X86) && !defined(ARCH_SH4)
# if defined(__x86_64__)
Expand Down Expand Up @@ -78,8 +84,12 @@ typedef unsigned char byte_t;

#define HOST_ELF_MACHINE {62, 3, 6, 0}
#define RED_ZONE_SIZE 128
#define OFFSETOF_STAT_DEV_32 0
#define OFFSETOF_STAT_INO_32 12
#define OFFSETOF_STAT_MODE_32 16
#define OFFSETOF_STAT_UID_32 24
#define OFFSETOF_STAT_GID_32 28
#define OFFSETOF_STAT_RDEV_32 32

#define LOADER_ADDRESS 0x600000000000
#define HAS_LOADER_32BIT true
Expand All @@ -101,8 +111,10 @@ typedef unsigned char byte_t;
#define user_regs_struct user_regs
#define HOST_ELF_MACHINE {40, 0};
#define RED_ZONE_SIZE 0
#define OFFSETOF_STAT_MODE_32 0
#define OFFSETOF_STAT_UID_32 0
#define OFFSETOF_STAT_GID_32 0
#define OFFSETOF_STAT_RDEV_32 0
#define EM_ARM 40

#define LOADER_ADDRESS 0x10000000
Expand All @@ -125,8 +137,10 @@ typedef unsigned char byte_t;

#define HOST_ELF_MACHINE {183, 0};
#define RED_ZONE_SIZE 0
#define OFFSETOF_STAT_MODE_32 0
#define OFFSETOF_STAT_UID_32 0
#define OFFSETOF_STAT_GID_32 0
#define OFFSETOF_STAT_RDEV_32 0

#define LOADER_ADDRESS 0x2000000000
#define EXEC_PIC_ADDRESS 0x3000000000
Expand All @@ -145,8 +159,10 @@ typedef unsigned char byte_t;

#define HOST_ELF_MACHINE {3, 6, 0};
#define RED_ZONE_SIZE 0
#define OFFSETOF_STAT_MODE_32 0
#define OFFSETOF_STAT_UID_32 0
#define OFFSETOF_STAT_GID_32 0
#define OFFSETOF_STAT_RDEV_32 0

#define LOADER_ADDRESS 0xa0000000
#define LOADER_ARCH_CFLAGS -mregparm=3
Expand All @@ -166,8 +182,10 @@ typedef unsigned char byte_t;
#define user_regs_struct pt_regs
#define HOST_ELF_MACHINE {42, 0};
#define RED_ZONE_SIZE 0
#define OFFSETOF_STAT_MODE_32 0
#define OFFSETOF_STAT_UID_32 0
#define OFFSETOF_STAT_GID_32 0
#define OFFSETOF_STAT_RDEV_32 0
#define NO_MISALIGNED_ACCESS 1

#else
Expand Down
197 changes: 174 additions & 23 deletions src/extension/fake_id0/fake_id0.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@
* 02110-1301 USA.
*/

#include <assert.h> /* assert(3), */
#include <stdint.h> /* intptr_t, */
#include <errno.h> /* E*, */
#include <sys/stat.h> /* chmod(2), stat(2) */
#include <sys/types.h> /* uid_t, gid_t, get*id(2), */
#include <unistd.h> /* get*id(2), */
#include <sys/ptrace.h> /* linux.git:c0a3a20b */
#include <linux/audit.h> /* AUDIT_ARCH_*, */
#include <string.h> /* memcpy(3), */
#include <stdlib.h> /* strtol(3), */
#include <linux/auxvec.h>/* AT_, */
#include <assert.h> /* assert(3), */
#include <stdint.h> /* intptr_t, */
#include <errno.h> /* E*, */
#include <sys/stat.h> /* chmod(2), stat(2) */
#include <sys/types.h> /* uid_t, gid_t, get*id(2), */
#include <unistd.h> /* get*id(2), */
#include <sys/ptrace.h> /* linux.git:c0a3a20b */
#include <linux/audit.h> /* AUDIT_ARCH_*, */
#include <string.h> /* memcpy(3), */
#include <stdlib.h> /* strtol(3), */
#include <linux/auxvec.h> /* AT_, */
#include <sys/sysmacros.h> /* makedev, major, minor */

#include "extension/extension.h"
#include "syscall/syscall.h"
Expand All @@ -44,6 +45,18 @@
#include "path/binding.h"
#include "arch.h"

typedef struct {
unsigned int dev_major, dev_minor, rdev_major, rdev_minor;
ino_t ino;
mode_t file_type;
int fd;
} Node;

typedef struct {
Node *ptr;
size_t len, cap;
} NodeList;

typedef struct {
uid_t ruid;
uid_t euid;
Expand All @@ -54,6 +67,8 @@ typedef struct {
gid_t egid;
gid_t sgid;
gid_t fsgid;

NodeList *nodes;
} Config;

typedef struct {
Expand Down Expand Up @@ -579,12 +594,74 @@ static int handle_sysexit_end(Tracee *tracee, Config *config)
POKE_MEM_ID(SYSARG_3, sgid);
return 0;

case PR_mknod:
case PR_mknodat:
/* Override only permission errors. */
result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
if ((int) result == -EPERM && config->euid == 0) {
char old_path[PATH_MAX], path[PATH_MAX];
struct stat statbuf;
int dirfd, fd, status;
mode_t mode;
dev_t dev;

if(sysnum == PR_mknod) {
dirfd = AT_FDCWD;
status = get_sysarg_path(tracee, old_path, SYSARG_1);
if (status < 0)
return 0;
status = translate_path(tracee, path, dirfd, old_path, 0);
if (status < 0)
return 0;
mode = peek_reg(tracee, ORIGINAL, SYSARG_2);
dev = peek_reg(tracee, ORIGINAL, SYSARG_3);
} else {
dirfd = peek_reg(tracee, ORIGINAL, SYSARG_1);
status = get_sysarg_path(tracee, old_path, SYSARG_2);
if (status < 0)
return 0;
status = translate_path(tracee, path, dirfd, old_path, 0);
if (status < 0)
return 0;
mode = peek_reg(tracee, ORIGINAL, SYSARG_3);
dev = peek_reg(tracee, ORIGINAL, SYSARG_4);
}

if (!S_ISCHR(mode) && !S_ISBLK(mode))
return 0;

fd = creat(path, mode & ~S_IFMT);
if (fd < 0)
return 0;

status = fstat(fd, &statbuf);
if (status < 0) {
close(fd);
return 0;
}

if (config->nodes->cap == config->nodes->len) {
config->nodes->cap *= 2;
config->nodes->ptr = realloc(config->nodes->ptr, sizeof(Node) * config->nodes->cap);
}
config->nodes->ptr[config->nodes->len++] = (Node) {
.dev_major = major(statbuf.st_dev),
.dev_minor = minor(statbuf.st_dev),
.rdev_major = major(dev),
.rdev_minor = minor(dev),
.ino = statbuf.st_ino,
.file_type = mode & S_IFMT,
.fd = fd,
};

poke_reg(tracee, SYSARG_RESULT, 0);
return 0;
}
/* Fall through. */
case PR_setdomainname:
case PR_sethostname:
case PR_setgroups:
case PR_setgroups32:
case PR_mknod:
case PR_mknodat:
case PR_capset:
case PR_setxattr:
case PR_lsetxattr:
Expand Down Expand Up @@ -625,10 +702,17 @@ static int handle_sysexit_end(Tracee *tracee, Config *config)
case PR_fstat: {
word_t address;
Reg sysarg;
ino_t ino;
mode_t mode;
uid_t uid;
gid_t gid;
dev_t dev_tmp;
unsigned int dev_major, dev_minor;
off_t ino_offset;
off_t mode_offset;
off_t uid_offset;
off_t gid_offset;
size_t i;

/* Override only if it succeed. */
result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
Expand All @@ -638,6 +722,8 @@ static int handle_sysexit_end(Tracee *tracee, Config *config)
/* Get the address of the 'stat' structure. */
if (sysnum == PR_statx) {
sysarg = SYSARG_5;
ino_offset = OFFSETOF_STATX_INO;
mode_offset = OFFSETOF_STATX_MODE;
uid_offset = OFFSETOF_STATX_UID;
gid_offset = OFFSETOF_STATX_GID;
}
Expand All @@ -646,17 +732,25 @@ static int handle_sysexit_end(Tracee *tracee, Config *config)
sysarg = SYSARG_3;
else
sysarg = SYSARG_2;
ino_offset = offsetof_stat_ino(tracee);
mode_offset = offsetof_stat_mode(tracee);
uid_offset = offsetof_stat_uid(tracee);
gid_offset = offsetof_stat_gid(tracee);
}

address = peek_reg(tracee, ORIGINAL, sysarg);

/* Sanity checks. */
assert(__builtin_types_compatible_p(ino_t, uint64_t));
assert(__builtin_types_compatible_p(uid_t, uint32_t));
assert(__builtin_types_compatible_p(gid_t, uint32_t));
assert(__builtin_types_compatible_p(mode_t, uint32_t));

/* Get the ino, uid, and gid values from the 'stat' structure. */
ino = peek_uint64(tracee, address + ino_offset);
if (errno != 0)
ino = 0; /* Not fatal. */

/* Get the uid & gid values from the 'stat' structure. */
uid = peek_uint32(tracee, address + uid_offset);
if (errno != 0)
uid = 0; /* Not fatal. */
Expand All @@ -665,6 +759,29 @@ static int handle_sysexit_end(Tracee *tracee, Config *config)
if (errno != 0)
gid = 0; /* Not fatal. */

/* Get the dev_major and dev_minor values from the 'stat' structure. */
if (sysnum == PR_statx) {
mode = peek_uint16(tracee, address + mode_offset);
if (errno != 0)
mode = 0;
dev_major = peek_uint32(tracee, address + OFFSETOF_STATX_DEV_MAJOR);
if (errno != 0)
dev_major = 0;
dev_minor = peek_uint32(tracee, address + OFFSETOF_STATX_DEV_MINOR);
if (errno != 0)
dev_minor = 0;
}
else {
mode = peek_uint32(tracee, address + mode_offset);
if (errno != 0)
mode = 0;
dev_tmp = peek_uint32(tracee, address + offsetof_stat_dev(tracee));
if (errno != 0)
dev_tmp = 0; /* Not fatal. */
dev_major = major(dev_tmp);
dev_minor = minor(dev_tmp);
}

/* Override only if the file is owned by the current user.
* Errors are not fatal here. */
if (uid == getuid())
Expand All @@ -673,17 +790,44 @@ static int handle_sysexit_end(Tracee *tracee, Config *config)
if (gid == getgid())
poke_uint32(tracee, address + gid_offset, config->sgid);

return 0;
}
/* If the file is one we created with mknod or mknodat, replace
* its file type and rdev. */
for (i = 0; i < config->nodes->len; i++) {
if (config->nodes->ptr[i].dev_major == dev_major
&& config->nodes->ptr[i].dev_minor == dev_minor
&& config->nodes->ptr[i].ino == ino) {
if (sysnum == PR_statx) {
poke_uint16(tracee, address + mode_offset,
(mode & ~S_IFMT) | config->nodes->ptr[i].file_type);
poke_uint32(tracee, address + OFFSETOF_STATX_RDEV_MAJOR,
config->nodes->ptr[i].rdev_major);
poke_uint32(tracee, address + OFFSETOF_STATX_RDEV_MINOR,
config->nodes->ptr[i].rdev_minor);
} else {
poke_uint32(tracee, address + mode_offset,
(mode & ~S_IFMT) | config->nodes->ptr[i].file_type);
poke_uint32(tracee, address + offsetof_stat_rdev(tracee),
makedev(config->nodes->ptr[i].rdev_major,
config->nodes->ptr[i].rdev_minor));
poke_uint32(tracee, address + offsetof_stat_rdev(tracee),
makedev(config->nodes->ptr[i].rdev_major,
config->nodes->ptr[i].rdev_minor));
}
break;
}
}

case PR_chroot: {
char path[PATH_MAX];
char abspath[PATH_MAX];
word_t input;
int status;
return 0;
}

if (config->euid != 0) /* TODO: && !HAS_CAP(SYS_CHROOT) */
return 0;
case PR_chroot: {
char path[PATH_MAX];
char abspath[PATH_MAX];
word_t input;
int status;

if (config->euid != 0) /* TODO: && !HAS_CAP(SYS_CHROOT) */
return 0;

/* Override only permission errors. */
result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
Expand Down Expand Up @@ -815,6 +959,13 @@ int fake_id0_callback(Extension *extension, ExtensionEvent event, intptr_t data1
config->sgid = gid;
config->fsgid = gid;

config->nodes = talloc(extension, NodeList);
if (config->nodes == NULL)
return -1;
config->nodes->cap = 8;
config->nodes->len = 0;
config->nodes->ptr = malloc(sizeof(Node) * config->nodes->cap);

extension->filtered_sysnums = filtered_sysnums;
return 0;
}
Expand Down
Loading

0 comments on commit b694332

Please sign in to comment.