mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 00:44:55 -06:00
* link: Collect WSL logs (recommended method) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Advanced Authoring Tests in C++ Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: CMake Documentation and Community Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Collect WSL logs for networking issues Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Collect WSL logs (recommended method) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: ; otherwise, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: a Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: access Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: accessible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: across Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: actively Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: adapters Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: address Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: addresses Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: and Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: appropriate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: argument Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: associated Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: attach Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: available Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: beginning Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: between Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: binaries Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: bound Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: buffer Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: buffers Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: cannot Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: canonical Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: capabilities Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: case-insensitive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: case-sensitive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: certified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: command Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: committer Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: communication Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: complains Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: configuration Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: consumed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: continue Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: converted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: currently Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: customers Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: daemon Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: deferred Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: definitions Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: delimiter Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: delivered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: dellink Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: derived Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: descriptor Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destined Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destruct Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destructible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: destructor Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: detach Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: differentiate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: directories Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disassociate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disposition Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: distribution Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: distro Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: duping Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: emitted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: empty Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: environment Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: every time Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: exclusive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: expected Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: expire Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: explicitly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: fall back Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: false Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: fastfail Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: filesystem Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: first Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: followed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: for Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: functionality Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: functionally Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: github Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: greater Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guarantee Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guaranteed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: handles Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hangup Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hierarchy Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hogwarts Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: hydrated Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: icrnl Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: implementation Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: implementing Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: initialize Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instance Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instantiate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: instantiations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: intentionally Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: interpret Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: interpreter Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: irreversibly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: iteration Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: iterator Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: its Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: kernel Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: kmsg Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: knowledge Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: maximum Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: mirrored Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: msftconnecttest Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: multi Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: multiple Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: mutable Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: namespace Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: nonexistent Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: notifications Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: occurred Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: occurring Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: otherwise, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: outstanding Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: overridden Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: partition Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: pass through Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: passthrough Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: performs Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: periodically Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: positional Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: precedence Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: preexisting Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: preferring Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: prepopulate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: previous Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: privileges Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: process Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: processes Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: programmatically Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: protection Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: provided Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: reasonable Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: receive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: received Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: red hat Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: reentrant Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: registered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: regularly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: relay Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: release Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: representing Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: requests Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: response Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: resurrect Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: retention Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: returned Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: security Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: semaphore Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: separate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: separator Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: service Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: set up Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: setup Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: severely Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: should Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: signal Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: similarly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: simple Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: simplified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: single Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: specified Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: splitting Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: standard Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: stress Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: succeed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: success Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: successfully Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: supplementary Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: synced Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: system Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: take Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: than Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: that opening Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: the Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: threadpool Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: to Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: true Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: truncate Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: tunneling Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unexpected Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: uninitialize Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unique Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unprivileged Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: unregistered Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: untrusted Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: upgrade Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: utility Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: validating Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variant Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variation Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: variations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: verify Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: visible Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: whether Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: winget Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: worker Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: written Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: wslservice Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * format source --------- Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Co-authored-by: Ben Hillis <benhillis@gmail.com> Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
2613 lines
72 KiB
C
2613 lines
72 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
vfsaccess.c
|
|
|
|
Abstract:
|
|
|
|
This file is a vfs access permissions test.
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/xattr.h>
|
|
#include <utime.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <sys/wait.h>
|
|
#include <limits.h>
|
|
#include "lxtfs.h"
|
|
|
|
#if !defined(__amd64__) && !defined(__aarch64__)
|
|
|
|
#include <sys/capability.h>
|
|
|
|
#else
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <linux/capability.h>
|
|
|
|
#define _LINUX_CAPABILITY_VERSION_3 0x20080522
|
|
|
|
#ifndef O_PATH
|
|
#define O_PATH 010000000
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#include <sys/vfs.h>
|
|
#include <linux/prctl.h>
|
|
#include <stdio.h>
|
|
|
|
#define LXT_NAME "vfsaccess"
|
|
#define LXT_NAME_DRVFS "vfsaccess_drvfs"
|
|
|
|
#define VFS_FILE_OBJECT_COUNT LXT_COUNT_OF(g_VfsFileObjectFlags)
|
|
|
|
#define VFS_FILE_CONTENTS "vfsaccesstestfilecontents"
|
|
|
|
#define VFS_ACCESS_UID 1012
|
|
|
|
#define VFS_ACCESS_PARENT_DIR "/data/test/vfsaccesstest"
|
|
|
|
#define VFS_ACCESS_CHMOD_DIR VFS_ACCESS_PARENT_DIR "/vfsaccessdir_chmod"
|
|
|
|
#define VFS_ACCESS_OPATH_DIR VFS_ACCESS_PARENT_DIR "/vfsaccessopathdir"
|
|
|
|
#define VFS_ACCESS_OPATH_FILE VFS_ACCESS_PARENT_DIR "/vfsaccessopath"
|
|
|
|
#define VFS_ACCESS_OPATH_FILE_LINK VFS_ACCESS_PARENT_DIR "/vfsaccessopathlink"
|
|
|
|
#define VFS_ACCESS_STICKY_BIT_DIR VFS_ACCESS_PARENT_DIR "/vfsaccessdir_stickybit"
|
|
|
|
#define VFS_ACCESS_GROUP_USER_ID_DIR VFS_ACCESS_PARENT_DIR "/vfsaccessdir_groupuserid"
|
|
|
|
#define VFS_ACCESS_UTIME_FILE VFS_ACCESS_PARENT_DIR "/vfsaccesutime"
|
|
|
|
#define VFS_ACCESS_FSUID_FILE VFS_ACCESS_PARENT_DIR "/setfsuid_testfile"
|
|
|
|
#define VFS_ACCESS_FIFO VFS_ACCESS_PARENT_DIR "/vfsaccess_fifo"
|
|
|
|
#define O_NOACCESS (O_WRONLY | O_RDWR)
|
|
|
|
#define VFS_ACCESS_EXECVE_TEST_RESULT (123)
|
|
|
|
typedef struct _VFS_ACCESS_FILE
|
|
{
|
|
char* Name;
|
|
mode_t Mode;
|
|
} VFS_ACCESS_FILE, *PVFS_ACCESS_FILE;
|
|
|
|
typedef struct _VFS_ACCESS_FILE_OBJECT
|
|
{
|
|
int Fd;
|
|
int Flags;
|
|
} VFS_ACCESS_FILE_OBJECT, *PVFS_ACCESS_FILE_OBJECT;
|
|
|
|
struct reuid_t
|
|
{
|
|
uid_t r;
|
|
uid_t e;
|
|
uid_t s;
|
|
};
|
|
|
|
int VfsAccessCheckResult(int ResultActual, int ResultExpected, int ErrnoActual, int ErrnoExpected, char* Message, int VariationIndex);
|
|
|
|
void VfsAccessFileObjectCleanup(void);
|
|
|
|
int VfsAccessFileObjectCreateFiles(void);
|
|
|
|
int VfsAccessFileObjectCreateSymlinks(void);
|
|
|
|
int VfsAccessFileObjectOpenFiles(VFS_ACCESS_FILE_OBJECT Files[]);
|
|
|
|
int VfsAccessFileObjectOpenSymlinks(VFS_ACCESS_FILE_OBJECT Files[]);
|
|
|
|
int VfsAccessFileObjectChecks(PLXT_ARGS Args);
|
|
|
|
int VfsAccessFileObjectSymlinksChecks(PLXT_ARGS Args);
|
|
|
|
int VfsAccessRemapReference(PLXT_ARGS Args);
|
|
|
|
int VfsAccessChmod(PLXT_ARGS Args);
|
|
|
|
int VfsAccessChmodCap(PLXT_ARGS Args);
|
|
|
|
LXT_VARIATION_HANDLER VfsAccessFifo;
|
|
|
|
int VfsAccessOPath(PLXT_ARGS Args);
|
|
|
|
int VfsAccessStickyBit(PLXT_ARGS Args);
|
|
|
|
int VfsAccessSetUserGroupId(PLXT_ARGS Args);
|
|
|
|
int VfsAccessSetUserGroupIdExecveChild(void);
|
|
|
|
int VfsAccessInodeChecks(PLXT_ARGS Args);
|
|
|
|
int VfsAccessParseArgs(int Argc, char* Argv[], LXT_ARGS* Args);
|
|
|
|
int VfsAccessUTimeCap(PLXT_ARGS Args);
|
|
|
|
int VfsAccessSetFsUid(PLXT_ARGS Args);
|
|
|
|
int VfsAccessSetUid(PLXT_ARGS Args);
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
|
|
#define VFS_ACCESS_FILE_OBJECT_FILE 0
|
|
#define VFS_ACCESS_REMAP_FILE 1
|
|
|
|
static const VFS_ACCESS_FILE g_VfsFiles[] = {
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessfile", S_IRWXU | S_IRWXG | S_IRWXO},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessfile_remap", S_IRWXU | S_IRWXG | S_IRWXO}};
|
|
|
|
static const char* g_VfsSymlinks[] = {
|
|
VFS_ACCESS_PARENT_DIR "/sym_vfsaccessfile", VFS_ACCESS_PARENT_DIR "/sym_vfsaccessfile_remap"};
|
|
|
|
static const int g_VfsFileObjectFlags[] = {O_RDONLY, O_WRONLY, O_RDWR, O_NOACCESS, O_RDONLY | O_PATH, O_RDWR | O_APPEND};
|
|
|
|
static const VFS_ACCESS_FILE g_VfsInodeEntries[] = {
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessfile_r", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessfile_w", S_IFREG | S_IWUSR | S_IWGRP | S_IWOTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessfile_x", S_IFREG | S_IXUSR | S_IXGRP | S_IXOTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessfile_rw", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessdir_r", S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessdir_w", S_IFDIR | S_IWUSR | S_IWGRP | S_IWOTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessdir_x", S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH},
|
|
{VFS_ACCESS_PARENT_DIR "/vfsaccessdir_wx", S_IFDIR | S_IWUSR | S_IWGRP | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH}};
|
|
|
|
#define VFS_ACCESS_INODE_ENTRY_FILE "vfsaccessfile"
|
|
|
|
int g_VfsSetFsUidCaps[] = {
|
|
CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID, CAP_LINUX_IMMUTABLE, CAP_MAC_OVERRIDE, CAP_MKNOD};
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {
|
|
{"VfsAccess file object checks", VfsAccessFileObjectChecks},
|
|
{"VfsAccess symlinks checks", VfsAccessFileObjectSymlinksChecks},
|
|
{"VfsAccess remap reference", VfsAccessRemapReference},
|
|
{"VfsAccess chmod", VfsAccessChmod},
|
|
{"VfsAccess chmod cap", VfsAccessChmodCap},
|
|
{"VfsAccess O_PATH", VfsAccessOPath},
|
|
{"VfsAccess sticky bit", VfsAccessStickyBit},
|
|
{"VfsAccess set-user-ID set-group-ID", VfsAccessSetUserGroupId},
|
|
{"VfsAccess inode checks", VfsAccessInodeChecks},
|
|
{"VfsAccess utime cap", VfsAccessUTimeCap},
|
|
{"VfsAccess setfsuid", VfsAccessSetFsUid},
|
|
//{"VfsAccess Fifo", VfsAccessFifo},
|
|
{"VfsAccess set*uid", VfsAccessSetUid}};
|
|
|
|
bool g_UseDrvFs = false;
|
|
|
|
int VfsAccessTestEntry(int Argc, char* Argv[])
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
LXT_ARGS Args;
|
|
int Result;
|
|
|
|
if ((Argc == 2) && (strcmp(Argv[1], "execvetest") == 0))
|
|
{
|
|
return VFS_ACCESS_EXECVE_TEST_RESULT;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessParseArgs(Argc, Argv, &Args));
|
|
|
|
ErrorExit:
|
|
LxtUninitialize();
|
|
return !LXT_SUCCESS(Result);
|
|
}
|
|
|
|
void VfsAccessFileObjectCleanup(void)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine cleans up test files.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsSymlinks); Index += 1)
|
|
{
|
|
unlink(g_VfsSymlinks[Index]);
|
|
}
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsFiles); ++Index)
|
|
{
|
|
unlink(g_VfsFiles[Index].Name);
|
|
}
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsInodeEntries); ++Index)
|
|
{
|
|
if (S_ISREG(g_VfsInodeEntries[Index].Mode))
|
|
{
|
|
unlink(g_VfsInodeEntries[Index].Name);
|
|
}
|
|
else
|
|
{
|
|
rmdir(g_VfsInodeEntries[Index].Name);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int VfsAccessFileObjectCreateFiles(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
unsigned int BytesWritten;
|
|
unsigned int Index;
|
|
int Fd;
|
|
int Result;
|
|
|
|
Fd = -1;
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsFiles); ++Index)
|
|
{
|
|
unlink(g_VfsFiles[Index].Name);
|
|
LxtCheckErrno(Fd = open(g_VfsFiles[Index].Name, O_RDWR | O_CREAT, g_VfsFiles[Index].Mode));
|
|
LxtCheckErrno(write(Fd, VFS_FILE_CONTENTS, sizeof(VFS_FILE_CONTENTS)));
|
|
if (Index == VFS_ACCESS_REMAP_FILE)
|
|
{
|
|
BytesWritten = sizeof(VFS_FILE_CONTENTS);
|
|
while (BytesWritten < 2 * PAGE_SIZE)
|
|
{
|
|
LxtCheckErrno(write(Fd, VFS_FILE_CONTENTS, sizeof(VFS_FILE_CONTENTS)));
|
|
BytesWritten += sizeof(VFS_FILE_CONTENTS);
|
|
}
|
|
}
|
|
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
}
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsInodeEntries); ++Index)
|
|
{
|
|
if (S_ISREG(g_VfsInodeEntries[Index].Mode))
|
|
{
|
|
unlink(g_VfsInodeEntries[Index].Name);
|
|
LxtCheckErrno(Fd = open(g_VfsInodeEntries[Index].Name, O_RDWR | O_CREAT, g_VfsInodeEntries[Index].Mode));
|
|
|
|
LxtCheckErrno(write(Fd, VFS_FILE_CONTENTS, sizeof(VFS_FILE_CONTENTS)));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
}
|
|
else
|
|
{
|
|
rmdir(g_VfsInodeEntries[Index].Name);
|
|
LxtCheckErrno(mkdir(g_VfsInodeEntries[Index].Name, g_VfsInodeEntries[Index].Mode));
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessFileObjectCreateSymlinks(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
unsigned int Index;
|
|
int IntermediateResult;
|
|
int Result;
|
|
|
|
LxtCheckEqual(LXT_COUNT_OF(g_VfsFiles), LXT_COUNT_OF(g_VfsSymlinks), "%d");
|
|
|
|
//
|
|
// Create symlinks for the files.
|
|
//
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsFiles); Index += 1)
|
|
{
|
|
IntermediateResult = symlink(g_VfsFiles[Index].Name, g_VfsSymlinks[Index]);
|
|
|
|
//
|
|
// The symlink call may fail if the symlink already exists. This is ok
|
|
// in order to run the unit test on the same machine multiple times.
|
|
//
|
|
|
|
if (IntermediateResult < 0)
|
|
{
|
|
LxtCheckErrnoFailure(IntermediateResult, EEXIST);
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessCheckResult(int ResultActual, int ResultExpected, int ErrnoActual, int ErrnoExpected, char* Message, int VariationIndex)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
if (ResultActual != ResultExpected)
|
|
{
|
|
if (ResultActual >= 0)
|
|
{
|
|
ErrnoActual = 0;
|
|
}
|
|
|
|
LxtLogError("Unexpected %s (%d) result actual %d (%s) != expected %d", Message, VariationIndex, ResultActual, strerror(ErrnoActual), ResultExpected);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((ResultActual == -1) && (ErrnoActual != ErrnoExpected))
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected %s (%d) errno actual %s != expected %s", Message, VariationIndex, strerror(ErrnoActual), strerror(ErrnoExpected));
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessFifo(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests access permissions on fifos.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int Result;
|
|
|
|
LxtCheckErrnoZeroSuccess(mkfifo(VFS_ACCESS_FIFO, 0600));
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
setgid(1000);
|
|
setuid(1000);
|
|
LxtCheckErrnoFailure(open(VFS_ACCESS_FIFO, O_RDONLY | O_NONBLOCK), EACCES);
|
|
|
|
LxtCheckErrnoFailure(open(VFS_ACCESS_FIFO, O_WRONLY | O_NONBLOCK), EACCES);
|
|
|
|
exit(0);
|
|
}
|
|
|
|
LxtWaitPidPoll(ChildPid, 0);
|
|
|
|
ErrorExit:
|
|
unlink(VFS_ACCESS_FIFO);
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessFileObjectChecks(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int AccessMode;
|
|
char Buffer;
|
|
int ErrnoExpected;
|
|
VFS_ACCESS_FILE_OBJECT Files[VFS_FILE_OBJECT_COUNT];
|
|
unsigned int Index;
|
|
void* Map;
|
|
int Result;
|
|
int ResultActual;
|
|
int ResultExpected;
|
|
bool VirtiofsNoDax;
|
|
|
|
LxtLogInfo("Fs type %d with dax = %d\n", g_LxtFsInfo.FsType, g_LxtFsInfo.Flags.Dax);
|
|
VirtiofsNoDax = g_LxtFsInfo.FsType == LxtFsTypeVirtioFs && g_LxtFsInfo.Flags.Dax == 0;
|
|
memset(Files, -1, sizeof(Files));
|
|
LxtCheckResult(VfsAccessFileObjectOpenFiles(Files));
|
|
for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; ++Index)
|
|
{
|
|
|
|
//
|
|
// Validate read with a valid buffer.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
ResultActual = LxtRead(Files[Index].Fd, &Buffer, 1);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, "read", Index));
|
|
|
|
//
|
|
// Validate read with a invalid buffer and a size of 0. The size of 0
|
|
// should cause the buffer to not be checked.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = LxtRead(Files[Index].Fd, (void*)0x1, 0);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, "read", Index));
|
|
|
|
//
|
|
// Validate write with a valid buffer.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_WRONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
ResultActual = LxtWrite(Files[Index].Fd, &Buffer, 1);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, "write", Index));
|
|
|
|
//
|
|
// Validate write with a invalid buffer and a size of 0. The size of 0
|
|
// should cause the buffer to not be checked.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_WRONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = LxtWrite(Files[Index].Fd, (void*)0x1, 0);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, "write", Index));
|
|
|
|
//
|
|
// Validate map read shared and upgrading to write access.
|
|
//
|
|
// N.B. The Linux 9p client does not allow mapping shared if the file is
|
|
// opened for write.
|
|
//
|
|
// N.B. The virtiofs device relies on fuse mapping which only supports
|
|
// shared in the presence of DAX.
|
|
//
|
|
|
|
ErrnoExpected = EACCES;
|
|
if (Files[Index].Flags == O_PATH)
|
|
{
|
|
ErrnoExpected = EBADF;
|
|
}
|
|
else if (VirtiofsNoDax && (Files[Index].Flags == O_RDONLY || (Files[Index].Flags & O_ACCMODE) == O_RDWR))
|
|
{
|
|
ErrnoExpected = ENODEV;
|
|
}
|
|
|
|
ResultExpected = -1;
|
|
if (!VirtiofsNoDax && (((Files[Index].Flags & O_ACCMODE) == O_RDONLY) || ((Files[Index].Flags & O_ACCMODE) == O_RDWR)) &&
|
|
((Files[Index].Flags & O_PATH) == 0))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
Map = mmap(NULL, sizeof(Buffer), PROT_READ, MAP_SHARED, Files[Index].Fd, 0);
|
|
ResultActual = -1;
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultActual = 1;
|
|
}
|
|
|
|
LxtLogInfo("%d, %d, %d", Index, Files[Index].Flags, ResultExpected);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap read shared", Index));
|
|
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = mprotect(Map, sizeof(Buffer), PROT_WRITE);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap read shared mprotect", Index));
|
|
|
|
LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));
|
|
}
|
|
|
|
//
|
|
// Validate map read private and upgrading to write access.
|
|
//
|
|
// N.B. The Linux 9p client does not allow mapping shared if the file is
|
|
// opened for write.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
Map = mmap(NULL, sizeof(Buffer), PROT_READ, MAP_PRIVATE, Files[Index].Fd, 0);
|
|
ResultActual = -1;
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultActual = 1;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap read private", Index));
|
|
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = mprotect(Map, sizeof(Buffer), PROT_WRITE);
|
|
VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap read private mprotect", Index);
|
|
|
|
LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));
|
|
}
|
|
|
|
//
|
|
// Validate map write shared and private
|
|
//
|
|
// N.B. The virtiofs device relies on fuse mapping which only supports
|
|
// shared in the presence of DAX.
|
|
//
|
|
|
|
ErrnoExpected = EACCES;
|
|
if (Files[Index].Flags == O_PATH)
|
|
{
|
|
ErrnoExpected = EBADF;
|
|
}
|
|
else if (VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)
|
|
{
|
|
ErrnoExpected = ENODEV;
|
|
}
|
|
|
|
ResultExpected = -1;
|
|
if (!VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)
|
|
{
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
Map = mmap(NULL, sizeof(Buffer), PROT_WRITE, MAP_SHARED, Files[Index].Fd, 0);
|
|
ResultActual = -1;
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultActual = 1;
|
|
LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap write shared", Index));
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
Map = mmap(NULL, sizeof(Buffer), PROT_WRITE, MAP_PRIVATE, Files[Index].Fd, 0);
|
|
ResultActual = -1;
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultActual = 1;
|
|
LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap write private", Index));
|
|
|
|
LxtClose(Files[Index].Fd);
|
|
Files[Index].Fd = -1;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessFileObjectSymlinksChecks(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer;
|
|
int ErrnoExpected;
|
|
VFS_ACCESS_FILE_OBJECT Files[VFS_FILE_OBJECT_COUNT];
|
|
unsigned int Index;
|
|
void* Map;
|
|
int Result;
|
|
int ResultActual;
|
|
int ResultExpected;
|
|
bool VirtiofsNoDax;
|
|
|
|
LxtLogInfo("Fs type %d with dax = %d\n", g_LxtFsInfo.FsType, g_LxtFsInfo.Flags.Dax);
|
|
VirtiofsNoDax = g_LxtFsInfo.FsType == LxtFsTypeVirtioFs && g_LxtFsInfo.Flags.Dax == 0;
|
|
|
|
memset(Files, -1, sizeof(Files));
|
|
LxtCheckResult(VfsAccessFileObjectOpenSymlinks(Files));
|
|
for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; ++Index)
|
|
{
|
|
|
|
//
|
|
// Validate read
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_RDONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
ResultActual = LxtRead(Files[Index].Fd, &Buffer, 1);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, "read", Index));
|
|
|
|
//
|
|
// Validate write
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((Files[Index].Flags == O_WRONLY) || (Files[Index].Flags == O_RDWR) || (Files[Index].Flags == (O_RDWR | O_APPEND)))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
ResultActual = LxtWrite(Files[Index].Fd, &Buffer, 1);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EBADF, "write", Index));
|
|
|
|
//
|
|
// Validate map read
|
|
//
|
|
|
|
ErrnoExpected = EACCES;
|
|
if (Files[Index].Flags == O_PATH)
|
|
{
|
|
ErrnoExpected = EBADF;
|
|
}
|
|
else if (VirtiofsNoDax && (Files[Index].Flags == O_RDONLY || (Files[Index].Flags & O_ACCMODE) == O_RDWR))
|
|
{
|
|
ErrnoExpected = ENODEV;
|
|
}
|
|
|
|
ResultExpected = -1;
|
|
if (!VirtiofsNoDax && (((Files[Index].Flags & O_ACCMODE) == O_RDONLY) || ((Files[Index].Flags & O_ACCMODE) == O_RDWR)) &&
|
|
((Files[Index].Flags & O_PATH) == 0))
|
|
{
|
|
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
Map = mmap(NULL, sizeof(Buffer), PROT_READ, MAP_SHARED, Files[Index].Fd, 0);
|
|
ResultActual = -1;
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultActual = 1;
|
|
LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap read", Index));
|
|
|
|
//
|
|
// Validate map write
|
|
//
|
|
|
|
ErrnoExpected = EACCES;
|
|
if (Files[Index].Flags == O_PATH)
|
|
{
|
|
ErrnoExpected = EBADF;
|
|
}
|
|
else if (VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)
|
|
{
|
|
ErrnoExpected = ENODEV;
|
|
}
|
|
|
|
ResultExpected = -1;
|
|
if (!VirtiofsNoDax && (Files[Index].Flags & O_ACCMODE) == O_RDWR)
|
|
{
|
|
ResultExpected = 1;
|
|
}
|
|
|
|
Map = mmap(NULL, sizeof(Buffer), PROT_WRITE, MAP_SHARED, Files[Index].Fd, 0);
|
|
ResultActual = -1;
|
|
if (Map != MAP_FAILED)
|
|
{
|
|
ResultActual = 1;
|
|
LxtCheckErrno(LxtMunmap(Map, sizeof(Buffer)));
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "mmap write", Index));
|
|
|
|
LxtClose(Files[Index].Fd);
|
|
Files[Index].Fd = -1;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessFileObjectOpenFiles(VFS_ACCESS_FILE_OBJECT Files[])
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
unsigned int Index;
|
|
int Result;
|
|
|
|
for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; ++Index)
|
|
{
|
|
Files[Index].Flags = g_VfsFileObjectFlags[Index];
|
|
LxtCheckErrno(Files[Index].Fd = open(g_VfsFiles[VFS_ACCESS_FILE_OBJECT_FILE].Name, Files[Index].Flags, 0));
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessFileObjectOpenSymlinks(VFS_ACCESS_FILE_OBJECT Files[])
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
unsigned int Index;
|
|
int Result;
|
|
|
|
for (Index = 0; Index < VFS_FILE_OBJECT_COUNT; Index += 1)
|
|
{
|
|
Files[Index].Flags = g_VfsFileObjectFlags[Index];
|
|
LxtCheckErrno(Files[Index].Fd = open(g_VfsSymlinks[VFS_ACCESS_FILE_OBJECT_FILE], Files[Index].Flags, 0));
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessRemapReference(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
char Buffer;
|
|
int FdReadOnly = -1;
|
|
int FdReadWrite = -1;
|
|
int MapFlags;
|
|
void* MapResult;
|
|
void* MapReadOnly = NULL;
|
|
void* MapReadWrite = NULL;
|
|
void* RemappedMemory = NULL;
|
|
int Result;
|
|
bool VirtiofsNoDax;
|
|
|
|
VirtiofsNoDax = g_LxtFsInfo.FsType == LxtFsTypeVirtioFs && g_LxtFsInfo.Flags.Dax == 0;
|
|
MapFlags = VirtiofsNoDax ? MAP_PRIVATE : MAP_SHARED;
|
|
|
|
//
|
|
// Open and map a file whose only reference is read only and open second
|
|
// file descriptor and mapping read write.
|
|
//
|
|
|
|
LxtCheckErrno(FdReadOnly = open(g_VfsFiles[VFS_ACCESS_REMAP_FILE].Name, O_RDONLY, 0));
|
|
LxtCheckMapErrno(MapReadOnly = mmap(NULL, sizeof(Buffer), PROT_READ, MapFlags, FdReadOnly, 0));
|
|
LxtCheckErrno(FdReadWrite = open(g_VfsFiles[VFS_ACCESS_REMAP_FILE].Name, O_RDWR, 0));
|
|
LxtCheckMapErrno(MapReadWrite = mmap(NULL, sizeof(Buffer), PROT_READ | PROT_WRITE, MapFlags, FdReadWrite, 0));
|
|
LxtCheckMapErrno(RemappedMemory = mremap(MapReadWrite, sizeof(Buffer), PAGE_SIZE * 2, MREMAP_MAYMOVE));
|
|
|
|
ErrorExit:
|
|
if (FdReadOnly != -1)
|
|
{
|
|
if (MapReadOnly != NULL)
|
|
{
|
|
LxtMunmap(MapReadOnly, sizeof(Buffer));
|
|
}
|
|
|
|
LxtClose(FdReadOnly);
|
|
}
|
|
|
|
if (FdReadWrite != -1)
|
|
{
|
|
if (RemappedMemory != NULL)
|
|
{
|
|
LxtMunmap(RemappedMemory, PAGE_SIZE * 2);
|
|
}
|
|
else if (MapReadWrite != NULL)
|
|
{
|
|
LxtMunmap(MapReadWrite, sizeof(Buffer));
|
|
}
|
|
|
|
LxtClose(FdReadWrite);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessChmod(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int DirFd;
|
|
int Result;
|
|
struct stat StatBuf;
|
|
|
|
DirFd = -1;
|
|
|
|
//
|
|
// Set bits with chmod and then fchmod.
|
|
//
|
|
|
|
rmdir(VFS_ACCESS_CHMOD_DIR);
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_CHMOD_DIR, S_IRWXU));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXU | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXU | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXG));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXG | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXG | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXO));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXO | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXO | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXU));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXU | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXU | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(chmod(VFS_ACCESS_CHMOD_DIR, 0xffff));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_ISVTX | S_ISGID | S_ISUID | S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != All bits", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(DirFd = open(VFS_ACCESS_CHMOD_DIR, O_DIRECTORY | O_RDONLY, 0));
|
|
LxtCheckErrno(fchmod(DirFd, S_IRWXG));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXG | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXG | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(fchmod(DirFd, S_IRWXO));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXO | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXO | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(fchmod(DirFd, S_IRWXU));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_IRWXU | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != S_IRWXU | S_IFDIR", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(fchmod(DirFd, 0xffff));
|
|
LxtCheckErrno(stat(VFS_ACCESS_CHMOD_DIR, &StatBuf));
|
|
if (StatBuf.st_mode != (S_ISVTX | S_ISGID | S_ISUID | S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR))
|
|
{
|
|
LxtLogError("Unexpected mode %d != All bits", StatBuf.st_mode);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (DirFd != -1)
|
|
{
|
|
LxtClose(DirFd);
|
|
}
|
|
|
|
rmdir(VFS_ACCESS_CHMOD_DIR);
|
|
return Result;
|
|
}
|
|
|
|
void VfsAccessChmodCapChild(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
int Result;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
|
|
CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
|
|
CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
|
|
CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
|
|
CapData[0].effective = CapData[0].permitted;
|
|
CapData[1].effective = CapData[1].permitted;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have CAP_FOWNER.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(setgid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(setuid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
//
|
|
// TODO: Update the below when checks are enforced
|
|
//
|
|
|
|
//
|
|
// Try to chmod the directory to the current value.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXU), EPERM);
|
|
|
|
//
|
|
// Try to chmod on the directory without CAP_FOWNER to 0751.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(chmod(VFS_ACCESS_CHMOD_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH), EPERM);
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
_exit(Result);
|
|
}
|
|
|
|
int VfsAccessChmodCap(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int Result;
|
|
|
|
rmdir(VFS_ACCESS_CHMOD_DIR);
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_CHMOD_DIR, S_IRWXU));
|
|
ChildPid = fork();
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessChmodCapChild();
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
rmdir(VFS_ACCESS_CHMOD_DIR);
|
|
return Result;
|
|
}
|
|
|
|
void VfsAccessOPathChild(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[100];
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
int Fd;
|
|
int Result;
|
|
struct stat StatBuffer;
|
|
struct statfs StatFsBuffer;
|
|
struct timespec Times[2] = {{0, UTIME_NOW}, {0, UTIME_NOW}};
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
|
|
CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
|
|
CapData[0].effective = CapData[0].permitted;
|
|
CapData[1].effective = CapData[1].permitted;
|
|
Fd = -1;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have VFS related
|
|
// capabilities.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
//
|
|
// Open the file with O_PATH and check the behavior for the syscalls of
|
|
// interest.
|
|
//
|
|
|
|
LxtCheckErrno(Fd = open(VFS_ACCESS_OPATH_FILE, O_PATH, 0));
|
|
|
|
//
|
|
// Check syscalls that take a file descriptor should fail because O_PATH was
|
|
// specified.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(fchmod(Fd, 0), EBADF);
|
|
LxtCheckErrnoFailure(fchown(Fd, 0, 0), EBADF);
|
|
LxtCheckErrnoFailure(fsync(Fd), EBADF);
|
|
LxtCheckErrnoFailure(LxtGetdents64(Fd, (struct dirent*)Buffer, sizeof(Buffer)), EBADF);
|
|
LxtCheckErrnoFailure(futimens(Fd, Times), EBADF);
|
|
LxtCheckErrnoFailure(flistxattr(Fd, Buffer, sizeof(Buffer)), EBADF);
|
|
|
|
//
|
|
// Check syscalls that take a file descriptor should succeed even through
|
|
// O_PATH was specified.
|
|
//
|
|
|
|
LxtCheckErrno(fstat(Fd, &StatBuffer));
|
|
LxtCheckErrno(fstatfs(Fd, &StatFsBuffer));
|
|
|
|
//
|
|
// Check syscalls that should succeed on a directory with O_PATH specified.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(close(Fd));
|
|
LxtCheckErrno(Fd = open(VFS_ACCESS_OPATH_DIR, O_PATH | O_DIRECTORY));
|
|
LxtCheckErrnoZeroSuccess(fchdir(Fd));
|
|
|
|
//
|
|
// Chdir should still fail if execute permissions are removed.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(chmod(VFS_ACCESS_OPATH_DIR, 0));
|
|
LxtCheckErrnoFailure(fchdir(Fd), EACCES);
|
|
|
|
//
|
|
// Check syscalls that take a path should succeed because they do not
|
|
// require access to the file, but instead just the path.
|
|
//
|
|
|
|
LxtCheckErrno(chmod(VFS_ACCESS_OPATH_FILE, 0));
|
|
LxtCheckErrno(chown(VFS_ACCESS_OPATH_FILE, 0, 0));
|
|
LxtCheckErrno(stat(VFS_ACCESS_OPATH_FILE, &StatBuffer));
|
|
LxtCheckErrno(statfs(VFS_ACCESS_OPATH_FILE, &StatFsBuffer));
|
|
LxtCheckErrno(readlink(VFS_ACCESS_OPATH_FILE_LINK, Buffer, sizeof(Buffer)));
|
|
|
|
//
|
|
// Xattr is not supported on drvfs currently.
|
|
//
|
|
|
|
if (g_UseDrvFs == false)
|
|
{
|
|
LxtCheckErrno(listxattr(VFS_ACCESS_OPATH_FILE_LINK, Buffer, sizeof(Buffer)));
|
|
LxtCheckErrno(llistxattr(VFS_ACCESS_OPATH_FILE_LINK, Buffer, sizeof(Buffer)));
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
_exit(Result);
|
|
}
|
|
|
|
int VfsAccessOPath(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int Fd;
|
|
int Result;
|
|
|
|
unlink(VFS_ACCESS_OPATH_FILE);
|
|
unlink(VFS_ACCESS_OPATH_FILE_LINK);
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_OPATH_DIR, 0111));
|
|
LxtCheckErrno(Fd = open(VFS_ACCESS_OPATH_FILE, O_CREAT, 0));
|
|
LxtCheckErrno(symlink(VFS_ACCESS_OPATH_FILE, VFS_ACCESS_OPATH_FILE_LINK));
|
|
ChildPid = fork();
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessOPathChild();
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
unlink(VFS_ACCESS_OPATH_FILE);
|
|
unlink(VFS_ACCESS_OPATH_FILE_LINK);
|
|
rmdir(VFS_ACCESS_OPATH_DIR);
|
|
return Result;
|
|
}
|
|
|
|
void VfsAccessRenameCapChild(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
int File;
|
|
int Result;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
|
|
CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
|
|
CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
|
|
CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
|
|
CapData[0].effective = CapData[0].permitted;
|
|
CapData[1].effective = CapData[1].permitted;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have CAP_FOWNER.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(setgid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(setuid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
//
|
|
// Create a file and directory for the current user.
|
|
//
|
|
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_STICKY_BIT_DIR "/userdir1", S_IRWXU));
|
|
LxtCheckErrno(File = creat(VFS_ACCESS_STICKY_BIT_DIR "/userfile1", S_IRWXU));
|
|
close(File);
|
|
|
|
//
|
|
// Try to rename the file and directory to an existing entry without
|
|
// CAP_FOWNER.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(rename(VFS_ACCESS_STICKY_BIT_DIR "/userfile1", VFS_ACCESS_STICKY_BIT_DIR "/file1"), EPERM);
|
|
|
|
LxtCheckErrnoFailure(rename(VFS_ACCESS_STICKY_BIT_DIR "/userdir1", VFS_ACCESS_STICKY_BIT_DIR "/dir1"), EPERM);
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
|
|
rmdir(VFS_ACCESS_STICKY_BIT_DIR "/userdir1");
|
|
remove(VFS_ACCESS_STICKY_BIT_DIR "/userfile1");
|
|
_exit(Result);
|
|
}
|
|
|
|
void VfsAccessRmdirCapChild(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
int Result;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
|
|
CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
|
|
CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
|
|
CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
|
|
CapData[0].effective = CapData[0].permitted;
|
|
CapData[1].effective = CapData[1].permitted;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have CAP_FOWNER.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(setgid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(setuid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
//
|
|
// Try to remove the file and directory without CAP_FOWNER.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(remove(VFS_ACCESS_STICKY_BIT_DIR "/file1"), EPERM);
|
|
LxtCheckErrnoFailure(remove(VFS_ACCESS_STICKY_BIT_DIR "/dir1"), EPERM);
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
_exit(Result);
|
|
}
|
|
|
|
int VfsAccessStickyBit(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int ChildStatus;
|
|
int File = 0;
|
|
int Result;
|
|
|
|
//
|
|
// Create a directory with the sticky bit set and a file inside.
|
|
//
|
|
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_STICKY_BIT_DIR, S_IRWXU | S_ISVTX));
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_STICKY_BIT_DIR "/dir1", S_IRWXU));
|
|
LxtCheckErrno(File = creat(VFS_ACCESS_STICKY_BIT_DIR "/file1", S_IRWXU));
|
|
ChildPid = fork();
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessRmdirCapChild();
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
ChildPid = fork();
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessRenameCapChild();
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (File > 0)
|
|
{
|
|
close(File);
|
|
}
|
|
|
|
rmdir(VFS_ACCESS_STICKY_BIT_DIR "/dir1");
|
|
remove(VFS_ACCESS_STICKY_BIT_DIR "/file1");
|
|
rmdir(VFS_ACCESS_STICKY_BIT_DIR);
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessSetUserGroupIdExecveChild(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs the child process for VfsAccessSetUserGroupId.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
uid_t EffectiveGroup;
|
|
uid_t FilesystemGroup;
|
|
uid_t RealGroup;
|
|
uid_t SavedGroup;
|
|
int Result = LXT_RESULT_FAILURE;
|
|
uid_t EffectiveUser;
|
|
uid_t FilesystemUser;
|
|
uid_t RealUser;
|
|
uid_t SavedUser;
|
|
|
|
LxtLogInfo("Child executable starting");
|
|
|
|
//
|
|
// Get the user and group id and verify they match the expected.
|
|
//
|
|
|
|
LxtCheckResult(getresuid(&RealUser, &EffectiveUser, &SavedUser));
|
|
LxtCheckEqual(EffectiveUser, VFS_ACCESS_UID, "%u");
|
|
LxtCheckEqual(SavedUser, VFS_ACCESS_UID, "%u");
|
|
|
|
FilesystemUser = LxtSetfsuid(-1);
|
|
LxtCheckEqual(FilesystemUser, VFS_ACCESS_UID, "%u");
|
|
|
|
LxtCheckResult(getresgid(&RealGroup, &EffectiveGroup, &SavedGroup));
|
|
LxtCheckEqual(EffectiveGroup, VFS_ACCESS_UID, "%u");
|
|
LxtCheckEqual(SavedGroup, VFS_ACCESS_UID, "%u");
|
|
|
|
FilesystemGroup = LxtSetfsgid(-1);
|
|
LxtCheckEqual(FilesystemGroup, VFS_ACCESS_UID, "%u");
|
|
|
|
LxtLogInfo("Child executable finished");
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void VfsAccessSetUserGroupIdFSetIdChild(int Fd1)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
char* Data = "Test data";
|
|
int Fd2 = -1;
|
|
char Path[PATH_MAX];
|
|
int Result;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have VFS capabilities
|
|
// and is in the other user\group.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(setgid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(setuid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
// The plan 9 server cannot know about the uid change after open, so reopen
|
|
// the file with the new security context.
|
|
if (g_LxtFsInfo.FsType == LxtFsTypePlan9)
|
|
{
|
|
snprintf(Path, sizeof(Path), "/proc/self/fd/%d", Fd1);
|
|
LxtCheckResult(Fd2 = open(Path, O_WRONLY));
|
|
LxtCheckErrno(write(Fd2, Data, 1));
|
|
LxtCheckClose(Fd2);
|
|
}
|
|
else
|
|
{
|
|
LxtCheckErrno(write(Fd1, Data, 1));
|
|
}
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd2 >= 0)
|
|
{
|
|
close(Fd2);
|
|
}
|
|
|
|
_exit(Result);
|
|
}
|
|
|
|
void VfsAccessSetUserGroupIdChmodChild(char* FilePath, uid_t Uid, gid_t Gid)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct stat Buffer;
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
int Result;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
|
|
// CapData[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER);
|
|
CapData[0].effective = CapData[0].permitted;
|
|
CapData[1].effective = CapData[1].permitted;
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
LxtLogInfo("chown(%s, %d, %d)", FilePath, Uid, Gid);
|
|
LxtCheckErrnoFailure(chown(FilePath, Uid, Gid), EPERM);
|
|
LxtCheckErrno(stat(FilePath, &Buffer));
|
|
LxtCheckEqual((Buffer.st_mode & (S_ISUID | S_ISGID)), (S_ISUID | S_ISGID), "%o");
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
_exit(Result);
|
|
}
|
|
|
|
int VfsAccessSetUserGroupId(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char* Argv[4];
|
|
struct stat Buffer;
|
|
int ChildPid;
|
|
char* Envp[1];
|
|
mode_t ExpectedMode;
|
|
int Fd1 = 0;
|
|
int Fd2 = 0;
|
|
int Mode;
|
|
int Result;
|
|
|
|
rmdir(VFS_ACCESS_GROUP_USER_ID_DIR);
|
|
|
|
//
|
|
// Create a directory with the set-group-ID bit set.
|
|
//
|
|
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_GROUP_USER_ID_DIR, S_IRWXU));
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR, &Buffer));
|
|
LxtLogInfo("VFS_ACCESS_GROUP_USER_ID_DIR mode after mkdir %o", Buffer.st_mode);
|
|
|
|
//
|
|
// Change the owner of the directory and set the set-group-ID bit.
|
|
//
|
|
|
|
LxtCheckErrno(chown(VFS_ACCESS_GROUP_USER_ID_DIR, VFS_ACCESS_UID, VFS_ACCESS_UID));
|
|
LxtCheckErrno(chmod(VFS_ACCESS_GROUP_USER_ID_DIR, S_IRWXU | S_ISGID));
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR, &Buffer));
|
|
LxtLogInfo("VFS_ACCESS_GROUP_USER_ID_DIR mode after chmod %o", Buffer.st_mode);
|
|
|
|
//
|
|
// Create some files and child directories.
|
|
//
|
|
|
|
LxtCheckErrno(Fd1 = creat(VFS_ACCESS_GROUP_USER_ID_DIR "/file1", 0777 | S_IRWXU | S_ISGID | S_ISUID));
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR "/file1", &Buffer));
|
|
LxtLogInfo("VFS_ACCESS_GROUP_USER_ID_DIR /file1 mode after mkdir %o", Buffer.st_mode);
|
|
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1", S_IRWXU));
|
|
LxtCheckErrno(Fd2 = creat(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1/file2", S_IRWXU));
|
|
LxtCheckErrno(mkdir(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1/dir2", S_IRWXU));
|
|
|
|
//
|
|
// Validate the files and directories have the correct uid and gid.
|
|
//
|
|
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR "/file1", &Buffer));
|
|
if (Buffer.st_gid != VFS_ACCESS_UID)
|
|
{
|
|
LxtLogError("/file1 gid %u does not match expected %u", Buffer.st_gid, VFS_ACCESS_UID);
|
|
}
|
|
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1", &Buffer));
|
|
if (Buffer.st_gid != VFS_ACCESS_UID)
|
|
{
|
|
LxtLogError("/dir gid %u does not match expected %u", Buffer.st_gid, VFS_ACCESS_UID);
|
|
}
|
|
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1/file2", &Buffer));
|
|
if (Buffer.st_gid != VFS_ACCESS_UID)
|
|
{
|
|
LxtLogError("/dir1/file2 gid %u does not match expected %u", Buffer.st_gid, VFS_ACCESS_UID);
|
|
}
|
|
|
|
LxtCheckErrno(stat(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1/dir2", &Buffer));
|
|
if (Buffer.st_gid != VFS_ACCESS_UID)
|
|
{
|
|
LxtLogError("/dir1/dir2 gid %u does not match expected %u", Buffer.st_gid, VFS_ACCESS_UID);
|
|
}
|
|
|
|
//
|
|
// Validate the execute behavior of the set user id and group id bits.
|
|
// Make a copy of the current binary to use for the test.
|
|
//
|
|
|
|
// change Args->Argv[0] so that it points to the new single test binary design
|
|
Args->Argv[0] = WSL_UNIT_TEST_BINARY;
|
|
|
|
LxtCheckResult(LxtCopyFile(Args->Argv[0], VFS_ACCESS_PARENT_DIR "/wsl_unit_tests"));
|
|
|
|
LxtCheckErrno(chown(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", VFS_ACCESS_UID, VFS_ACCESS_UID));
|
|
|
|
LxtCheckErrno(stat(Args->Argv[0], &Buffer));
|
|
LxtCheckErrno(chmod(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", (Buffer.st_mode | S_ISUID | S_ISGID)));
|
|
|
|
ExpectedMode = Buffer.st_mode;
|
|
|
|
//
|
|
// Start the child process.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
Argv[0] = VFS_ACCESS_PARENT_DIR "/wsl_unit_tests";
|
|
Argv[1] = "vfsaccess";
|
|
Argv[2] = "-c";
|
|
Argv[3] = Envp[0] = NULL;
|
|
LxtCheckErrno(stat(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", &Buffer));
|
|
LxtLogInfo("child %o %u %u", Buffer.st_mode, Buffer.st_uid, Buffer.st_gid);
|
|
execve(Argv[0], Argv, Envp);
|
|
LxtLogError("Execve failed, errno: %d (%s)", errno, strerror(errno));
|
|
_exit(LXT_RESULT_FAILURE);
|
|
}
|
|
|
|
//
|
|
// Wait for the child to exit.
|
|
//
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
|
|
//
|
|
// Set the uid and gid again to make sure the set-user-id and set-group-id
|
|
// bits are stripped from the mode.
|
|
//
|
|
|
|
LxtCheckErrno(chown(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", -1, -1));
|
|
LxtCheckErrno(stat(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", &Buffer));
|
|
LxtCheckEqual(Buffer.st_mode, ExpectedMode, "0%o");
|
|
|
|
//
|
|
// Re-set the set-user-id and set-group-id bits.
|
|
//
|
|
|
|
LxtCheckErrno(chmod(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", (Buffer.st_mode | S_ISUID | S_ISGID)));
|
|
|
|
// VirtioFs does not currently handle capability flags. There is a new KILLPRIV2 FUSE flag that may address this in the future.
|
|
if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)
|
|
{
|
|
//
|
|
// Fork and drop privileges so the current process does not have CAP_FOWNER
|
|
// which is required for changing the owner of a file with the set-user-id
|
|
// or set-group-id bits set.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", VFS_ACCESS_UID, VFS_ACCESS_UID);
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", VFS_ACCESS_UID, -1);
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", -1, VFS_ACCESS_UID);
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessSetUserGroupIdChmodChild(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests", -1, -1);
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
}
|
|
|
|
//
|
|
// Validate the behavior of CAP_FSETID for files.
|
|
//
|
|
|
|
LxtLogInfo("Checking CAP_FSETID for files");
|
|
LxtCheckErrno(fstat(Fd1, &Buffer));
|
|
Mode = Buffer.st_mode;
|
|
if ((Mode & (S_ISGID | S_ISUID)) != (S_ISGID | S_ISUID))
|
|
{
|
|
LxtLogError("Unexpected mode");
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(write(Fd1, &Buffer, sizeof(Buffer)));
|
|
LxtCheckErrno(fstat(Fd1, &Buffer));
|
|
LxtCheckEqual(Buffer.st_mode, Mode, "%o");
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessSetUserGroupIdFSetIdChild(Fd1);
|
|
}
|
|
|
|
//
|
|
// Wait for the child to exit and validate that the set id bits were
|
|
// silently removed.
|
|
//
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
|
LxtCheckErrno(fstat(Fd1, &Buffer));
|
|
LxtCheckEqual(Buffer.st_mode, Mode & ~(S_ISGID | S_ISUID), "%o");
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd1 > 0)
|
|
{
|
|
LxtClose(Fd1);
|
|
}
|
|
|
|
if (Fd2 > 0)
|
|
{
|
|
LxtClose(Fd2);
|
|
}
|
|
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
//
|
|
// Clean-up created files and directories.
|
|
//
|
|
|
|
unlink(VFS_ACCESS_PARENT_DIR "/wsl_unit_tests");
|
|
remove(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1/file2");
|
|
rmdir(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1/dir2");
|
|
remove(VFS_ACCESS_GROUP_USER_ID_DIR "/file1");
|
|
rmdir(VFS_ACCESS_GROUP_USER_ID_DIR "/dir1");
|
|
rmdir(VFS_ACCESS_GROUP_USER_ID_DIR);
|
|
return Result;
|
|
}
|
|
|
|
void VfsAccessInodeChecksChild(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
char* CommandLine[] = {NULL, NULL};
|
|
int ErrnoExpected;
|
|
char FileName[100];
|
|
unsigned int Index;
|
|
int Result;
|
|
int ResultActual;
|
|
int ResultExpected;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have VFS capabilities
|
|
// and is in the other user\group.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(setgid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(setuid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
//
|
|
// For each file, check that read, write and execute is enforced. Similarly
|
|
// for directories check that list, create\delete, and search is enforced.
|
|
//
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsInodeEntries); ++Index)
|
|
{
|
|
if (S_ISREG(g_VfsInodeEntries[Index].Mode))
|
|
{
|
|
|
|
//
|
|
// Check read access.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0)
|
|
{
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = open(g_VfsInodeEntries[Index].Name, O_RDONLY, 0);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "file open O_RDONLY", Index));
|
|
|
|
//
|
|
// Check write access.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0)
|
|
{
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = open(g_VfsInodeEntries[Index].Name, O_WRONLY, 0);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "file open O_WRONLY", Index));
|
|
|
|
//
|
|
// Check read\write access.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if (((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0) && ((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = open(g_VfsInodeEntries[Index].Name, O_RDWR, 0);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "file open O_RDWR", Index));
|
|
|
|
//
|
|
// Check no access (open time check for read\write).
|
|
//
|
|
|
|
if (((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0) && ((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = open(g_VfsInodeEntries[Index].Name, O_NOACCESS, 0);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "file open O_NOACCESS", Index));
|
|
|
|
//
|
|
// Check execute access.
|
|
//
|
|
|
|
ErrnoExpected = EACCES;
|
|
ResultExpected = -1;
|
|
if ((g_VfsInodeEntries[Index].Mode & S_IXOTH) != 0)
|
|
{
|
|
ErrnoExpected = ENOEXEC;
|
|
}
|
|
|
|
CommandLine[0] = g_VfsInodeEntries[Index].Name;
|
|
ResultActual = execv(CommandLine[0], CommandLine);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "execv", Index));
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Check read access.
|
|
//
|
|
|
|
ResultExpected = -1;
|
|
if ((g_VfsInodeEntries[Index].Mode & S_IROTH) != 0)
|
|
{
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = open(g_VfsInodeEntries[Index].Name, O_RDONLY, 0);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "directory open O_RDONLY", Index));
|
|
|
|
//
|
|
// Check create\delete (write) access. Execute access is also
|
|
// required to create and delete.
|
|
//
|
|
|
|
sprintf(FileName, "%s/%s", g_VfsInodeEntries[Index].Name, VFS_ACCESS_INODE_ENTRY_FILE);
|
|
|
|
ResultExpected = -1;
|
|
if (((g_VfsInodeEntries[Index].Mode & S_IWOTH) != 0) && ((g_VfsInodeEntries[Index].Mode & S_IXOTH) != 0))
|
|
{
|
|
|
|
ResultExpected = 0;
|
|
}
|
|
|
|
ResultActual = open(FileName, O_CREAT | O_RDONLY, S_IRUSR);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "directory create file", Index));
|
|
|
|
if (ResultActual == 0)
|
|
{
|
|
ResultActual = unlink(FileName);
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, EACCES, "directory delete file", Index));
|
|
}
|
|
|
|
//
|
|
// Check search (execute) access.
|
|
//
|
|
|
|
ErrnoExpected = EACCES;
|
|
ResultExpected = -1;
|
|
if ((g_VfsInodeEntries[Index].Mode & S_IXOTH) != 0)
|
|
{
|
|
ErrnoExpected = ENOENT;
|
|
}
|
|
|
|
ResultActual = open(FileName, O_RDONLY, 0);
|
|
if (ResultActual != -1)
|
|
{
|
|
LxtClose(ResultActual);
|
|
ResultActual = 0;
|
|
}
|
|
|
|
LxtCheckResult(VfsAccessCheckResult(ResultActual, ResultExpected, errno, ErrnoExpected, "directory search file", Index));
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
_exit(Result);
|
|
}
|
|
|
|
int VfsAccessInodeChecks(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int Result;
|
|
|
|
ChildPid = fork();
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessInodeChecksChild();
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessParseArgs(int Argc, char* Argv[], LXT_ARGS* Args)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses command line arguments for the vfsaccess tests.
|
|
|
|
Arguments:
|
|
|
|
Argc - Supplies the number of arguments.
|
|
|
|
Argv - Supplies an array of arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ArgvIndex;
|
|
bool Cleanup;
|
|
const char* Name;
|
|
int Result;
|
|
int ValidArguments;
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
ValidArguments = 0;
|
|
g_UseDrvFs = false;
|
|
Name = LXT_NAME;
|
|
Cleanup = true;
|
|
if (Argc < 1)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
umask(0);
|
|
for (ArgvIndex = 1; ArgvIndex < Argc; ++ArgvIndex)
|
|
{
|
|
if (strcmp(Argv[ArgvIndex], "drvfs") == 0)
|
|
{
|
|
g_UseDrvFs = true;
|
|
Name = LXT_NAME_DRVFS;
|
|
continue;
|
|
}
|
|
|
|
if (Argv[ArgvIndex][0] != '-')
|
|
{
|
|
printf("Unexpected character %s", Argv[ArgvIndex]);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
switch (Argv[ArgvIndex][1])
|
|
{
|
|
case 'c':
|
|
|
|
//
|
|
// Run the setusergroupid execve test child
|
|
//
|
|
|
|
ValidArguments = 1;
|
|
Cleanup = false;
|
|
Result = VfsAccessSetUserGroupIdExecveChild();
|
|
goto ErrorExit;
|
|
|
|
case 'v':
|
|
case 'l':
|
|
|
|
//
|
|
// This was already taken care of by LxtInitialize.
|
|
//
|
|
|
|
++ArgvIndex;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
case 'a':
|
|
break;
|
|
|
|
default:
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If -c was not specified, just run the tests
|
|
//
|
|
|
|
ValidArguments = 1;
|
|
LxtCheckResult(LxtInitialize(Argc, Argv, Args, Name));
|
|
LxtCheckResult(LxtFsTestSetup(Args, VFS_ACCESS_PARENT_DIR, "/vfsaccesstest", g_UseDrvFs));
|
|
|
|
if (Args->HelpRequested == false)
|
|
{
|
|
LxtLogInfo("Creating files.");
|
|
LxtCheckResult(VfsAccessFileObjectCreateFiles());
|
|
LxtCheckResult(VfsAccessFileObjectCreateSymlinks());
|
|
}
|
|
|
|
//
|
|
// Tests must be run forked since some of the tests change the uid and
|
|
// don't change it back, which breaks umount during cleanup.
|
|
//
|
|
|
|
LxtCheckResult(LxtRunVariationsForked(Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
|
|
|
ErrorExit:
|
|
if (ValidArguments == 0)
|
|
{
|
|
printf("\nuse: %s <One of the below arguments>\n", Argv[0]);
|
|
printf("\t-c : Run %s execve test child (don't use directly)\n", Argv[0]);
|
|
}
|
|
|
|
if (Cleanup != false)
|
|
{
|
|
VfsAccessFileObjectCleanup();
|
|
LxtFsTestCleanup(VFS_ACCESS_PARENT_DIR, "/vfsaccesstest", g_UseDrvFs);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void VfsAccessUTimeCapChild(void)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
int Fd;
|
|
int Result;
|
|
struct timeval Times[2];
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
|
|
CapData[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
|
|
CapData[0].effective = CapData[0].permitted;
|
|
CapData[1].effective = CapData[1].permitted;
|
|
|
|
//
|
|
// Drop privileges so the current process does not have CAP_FOWNER.
|
|
//
|
|
|
|
LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1));
|
|
LxtCheckErrno(setgid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(setuid(VFS_ACCESS_UID));
|
|
LxtCheckErrno(LxtCapSet(&CapHeader, CapData));
|
|
|
|
//
|
|
// Create a file with a different user.
|
|
//
|
|
|
|
unlink(VFS_ACCESS_UTIME_FILE);
|
|
LxtCheckErrno(Fd = open(VFS_ACCESS_UTIME_FILE, O_CREAT, 0));
|
|
LxtClose(Fd);
|
|
LxtCheckErrno(chown(VFS_ACCESS_UTIME_FILE, VFS_ACCESS_UID + 1, VFS_ACCESS_UID + 1));
|
|
|
|
//
|
|
// Try to change the time on the file to 0.
|
|
//
|
|
|
|
memset(Times, 0, sizeof(Times));
|
|
LxtCheckErrnoFailure(utimes(VFS_ACCESS_UTIME_FILE, Times), EPERM);
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
unlink(VFS_ACCESS_UTIME_FILE);
|
|
_exit(Result);
|
|
}
|
|
|
|
int VfsAccessUTimeCap(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int Result;
|
|
|
|
ChildPid = fork();
|
|
if (ChildPid == 0)
|
|
{
|
|
VfsAccessUTimeCapChild();
|
|
}
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS));
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int VfsAccessSetFsUid(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
unsigned long long Effective;
|
|
struct __user_cap_data_struct CapData[2];
|
|
struct __user_cap_header_struct CapHeader;
|
|
struct __user_cap_data_struct ExpectedCapData[2];
|
|
int Fd;
|
|
int Index;
|
|
struct passwd* Password;
|
|
int Result;
|
|
|
|
memset(&CapData, 0, sizeof(CapData));
|
|
memset(&CapHeader, 0, sizeof(CapHeader));
|
|
CapHeader.version = _LINUX_CAPABILITY_VERSION_3;
|
|
|
|
Fd = -1;
|
|
Result = LXT_RESULT_FAILURE;
|
|
|
|
//
|
|
// Get the password entry for the 'nobody' user.
|
|
//
|
|
|
|
Password = getpwnam("nobody");
|
|
if (Password == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Create a file to be used for access checks.
|
|
//
|
|
|
|
Fd = open(VFS_ACCESS_FSUID_FILE, O_CREAT | O_RDWR, 0644);
|
|
if (Fd < 0)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Get the original capabilities.
|
|
//
|
|
|
|
LxtCheckErrno(LxtCapGet(&CapHeader, ExpectedCapData));
|
|
Effective = (((unsigned long long)ExpectedCapData[0].effective) << 32) | ExpectedCapData[1].effective;
|
|
LxtLogInfo("Before setfsuid(nobody) %016llX", Effective);
|
|
|
|
//
|
|
// Set the fsuid and ensure that the correct capabilities are dropped when
|
|
// switching from root.
|
|
//
|
|
|
|
if (LxtSetfsuid(Password->pw_uid) < 0)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(LxtCapGet(&CapHeader, CapData));
|
|
Effective = (((unsigned long long)CapData[0].effective) << 32) | CapData[1].effective;
|
|
LxtLogInfo("After setfsuid(nobody) %016llX", Effective);
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsSetFsUidCaps); Index++)
|
|
{
|
|
ExpectedCapData[CAP_TO_INDEX(g_VfsSetFsUidCaps[Index])].effective &= ~CAP_TO_MASK(g_VfsSetFsUidCaps[Index]);
|
|
}
|
|
|
|
if ((CapData[0].effective != ExpectedCapData[0].effective) || (CapData[1].effective != ExpectedCapData[1].effective))
|
|
{
|
|
|
|
LxtLogError("Capabilities do not match expected");
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Verify that opening the file fails since we no longer have the correct fsuid or capabilities.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(open(VFS_ACCESS_FSUID_FILE, O_RDWR), EACCES);
|
|
|
|
//
|
|
// Set the fsuid back to root and verify that the capabilities were correctly restored.
|
|
//
|
|
|
|
LxtCheckErrno(LxtSetfsuid(0));
|
|
|
|
LxtCheckErrno(LxtCapGet(&CapHeader, CapData));
|
|
Effective = (((unsigned long long)CapData[0].effective) << 32) | CapData[1].effective;
|
|
LxtLogInfo("After setfsuid(root) %016llX", Effective);
|
|
for (Index = 0; Index < LXT_COUNT_OF(g_VfsSetFsUidCaps); Index++)
|
|
{
|
|
ExpectedCapData[CAP_TO_INDEX(g_VfsSetFsUidCaps[Index])].effective |= CAP_TO_MASK(g_VfsSetFsUidCaps[Index]);
|
|
}
|
|
|
|
if ((CapData[0].effective != ExpectedCapData[0].effective) || (CapData[1].effective != ExpectedCapData[1].effective))
|
|
{
|
|
|
|
LxtLogError("Capabilities do not match expected");
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
unlink(VFS_ACCESS_FSUID_FILE);
|
|
return Result;
|
|
}
|
|
|
|
void getreuid(struct reuid_t* Set)
|
|
{
|
|
getresuid(&Set->r, &Set->e, &Set->s);
|
|
// LxtLogInfo("getresuid(%d,%d,%d)",Set->r,Set->e,Set->s);
|
|
}
|
|
|
|
pid_t fork_wait()
|
|
{
|
|
pid_t pid;
|
|
int status = 0;
|
|
if ((pid = fork()) == 0)
|
|
{
|
|
return pid;
|
|
}
|
|
else
|
|
{
|
|
waitpid(pid, &status, 0);
|
|
}
|
|
return pid;
|
|
}
|
|
|
|
int VfsAccessSetUid(PLXT_ARGS Args)
|
|
|
|
{
|
|
|
|
struct reuid_t Original;
|
|
struct reuid_t Set;
|
|
struct passwd* Nobody;
|
|
int Result;
|
|
|
|
getreuid(&Original);
|
|
LxtLogInfo("Current UID: %d", Original.r);
|
|
LxtLogInfo("Current EUID: %d", Original.e);
|
|
LxtLogInfo("Current SUID: %d", Original.s);
|
|
|
|
//
|
|
// Try setting without changing
|
|
//
|
|
|
|
setreuid(-1, -1);
|
|
getreuid(&Set);
|
|
LxtCheckEqual(Set.r, Original.r, "%d");
|
|
LxtCheckEqual(Set.e, Original.e, "%d");
|
|
LxtCheckEqual(Set.s, Original.s, "%d");
|
|
|
|
//
|
|
// More tests possible when run as root.
|
|
//
|
|
|
|
if (Original.r == 0 || Original.e == 0)
|
|
{
|
|
Nobody = getpwnam("nobody");
|
|
if (Nobody == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Couldn't get details for user 'nobody'");
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("Attempting setreuid(%d, -1)", Nobody->pw_uid);
|
|
LxtCheckResult(setreuid(Nobody->pw_uid, -1));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != 0 || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// reset state to 0, 0, 0
|
|
//
|
|
|
|
LxtLogInfo("setuid(0)");
|
|
LxtCheckResult(setuid(0));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != 0 || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
return -1; // Fatal, Nobody tests rely on this succeeding
|
|
}
|
|
|
|
//
|
|
// This test checks that setuid only touches the ruid and suid values.
|
|
//
|
|
|
|
LxtLogInfo("setresuid(-1, %d, %d)", Nobody->pw_uid, Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(-1, Nobody->pw_uid, Nobody->pw_uid));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != Nobody->pw_uid || Set.s != Nobody->pw_uid)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// Set state to 0, 0, 65534
|
|
//
|
|
|
|
LxtLogInfo("Attempting setuid(0)");
|
|
LxtCheckResult(setuid(0));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != 0 || Set.s != 65534)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// This test checks the first transitive property of setreuid wherein
|
|
// setting the effective uid also sets the suid
|
|
//
|
|
// Reset state to 0, 0, 0
|
|
//
|
|
|
|
LxtLogInfo("setresuid(0, 0, 0)");
|
|
LxtCheckResult(setresuid(0, 0, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != 0 || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
return -1; // Fatal, Other tests rely on this succeeding
|
|
}
|
|
LxtLogInfo("setreuid(-1, %d)", Nobody->pw_uid);
|
|
LxtCheckResult(setreuid(-1, Nobody->pw_uid));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != Nobody->pw_uid || Set.s != Nobody->pw_uid)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// Set state to 0, 0, 65534
|
|
//
|
|
|
|
LxtLogInfo("Attempting setuid(0)");
|
|
LxtCheckResult(setuid(0));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != 0 || Set.s != 65534)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// This test checks the second transitive property of setreuid
|
|
// wherein setting the ruid, but not the euid will Set the suid
|
|
// to be the euid
|
|
//
|
|
|
|
LxtLogInfo("setresuid(%d, 0, VFS_ACCESS_UID)", Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(Nobody->pw_uid, 0, VFS_ACCESS_UID));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != 0 || Set.s != VFS_ACCESS_UID)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
LxtLogInfo("Attempting setreuid(0, -1)");
|
|
LxtCheckResult(setreuid(0, -1));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != 0 || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// This test checks that unprivileged processes can Set the euid
|
|
// to the ruid or suid. Need to fork and wait as privileges are
|
|
// irreversibly dropped by this syscall
|
|
//
|
|
|
|
if (fork_wait() == 0)
|
|
{
|
|
LxtLogInfo("setresuid(%d, VFS_ACCESS_UID, 0)", Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(Nobody->pw_uid, VFS_ACCESS_UID, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
LxtLogInfo("Attempting setreuid(-1, %d)", Nobody->pw_uid);
|
|
LxtCheckResult(setreuid(-1, Nobody->pw_uid));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != Nobody->pw_uid || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
//
|
|
// This test checks that unprivileged processes can Set the euid
|
|
// to the ruid or suid.
|
|
//
|
|
|
|
LxtLogInfo("setresuid(%d, %d, 0)", Nobody->pw_uid, Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(Nobody->pw_uid, Nobody->pw_uid, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != Nobody->pw_uid || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
LxtLogInfo("Attempting setreuid(-1, 0)");
|
|
LxtCheckResult(setreuid(-1, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != 0 || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
|
|
//
|
|
// Reset state to 0, 0, 0
|
|
//
|
|
|
|
LxtLogInfo("setresuid(0, 0, 0)");
|
|
LxtCheckResult(setresuid(0, 0, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != 0 || Set.e != 0 || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
return -1; // Fatal, Other tests rely on this succeeding
|
|
}
|
|
|
|
//
|
|
// This test validates that unprivileged users can only Set the ruid
|
|
// to the ruid or the euid
|
|
//
|
|
|
|
if (fork_wait() == 0)
|
|
{
|
|
LxtLogInfo("setresuid(%d, VFS_ACCESS_UID, 0)", Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(Nobody->pw_uid, VFS_ACCESS_UID, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
LxtLogInfo("Attempting setreuid(VFS_ACCESS_UID, -1)");
|
|
LxtCheckResult(setreuid(VFS_ACCESS_UID, -1));
|
|
getreuid(&Set);
|
|
if (Set.r != VFS_ACCESS_UID || Set.e != VFS_ACCESS_UID || Set.s != VFS_ACCESS_UID)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
exit(0);
|
|
}
|
|
if (fork_wait() == 0)
|
|
{
|
|
LxtLogInfo("setresuid(%d, VFS_ACCESS_UID, 0)", Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(Nobody->pw_uid, VFS_ACCESS_UID, 0));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
LxtLogInfo("Attempting setreuid(0, -1)");
|
|
LxtCheckErrnoFailure(setreuid(0, -1), EPERM);
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != VFS_ACCESS_UID || Set.s != 0)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
//
|
|
// Drop all permissions permanently
|
|
//
|
|
|
|
LxtLogInfo("Dropping all permissions");
|
|
LxtLogInfo("setresuid(%d, %d, %d)", Nobody->pw_uid, Nobody->pw_uid, Nobody->pw_uid);
|
|
LxtCheckResult(setresuid(Nobody->pw_uid, Nobody->pw_uid, Nobody->pw_uid));
|
|
getreuid(&Set);
|
|
if (Set.r != Nobody->pw_uid || Set.e != Nobody->pw_uid)
|
|
{
|
|
LxtLogError("uid=%d, euid=%d, suid=%d", Set.r, Set.e, Set.s);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Try to gain root uid
|
|
//
|
|
|
|
LxtLogInfo("Attempting setreuid(0, -1)");
|
|
LxtCheckErrnoFailure(setreuid(0, -1), EPERM);
|
|
getreuid(&Set);
|
|
if (Set.r == 0 || Set.e == 0)
|
|
{
|
|
LxtLogError("Gained root permissions!");
|
|
}
|
|
LxtLogInfo("Attempting setreuid(-1, 0)");
|
|
LxtCheckErrnoFailure(setreuid(-1, 0), EPERM);
|
|
getreuid(&Set);
|
|
if (Set.r == 0 || Set.e == 0)
|
|
{
|
|
LxtLogError("Gained root permissions!");
|
|
}
|
|
LxtLogInfo("Attempting setreuid(0, 0)");
|
|
LxtCheckErrnoFailure(setreuid(0, 0), EPERM);
|
|
getreuid(&Set);
|
|
if (Set.r == 0 || Set.e == 0)
|
|
{
|
|
LxtLogError("Gained root permissions!");
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|