diff --git a/pfs/.clang-format b/pfs/.clang-format new file mode 100644 index 0000000..6fdf0c9 --- /dev/null +++ b/pfs/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: Google +AlignConsecutiveAssignments: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +ColumnLimit: 100 +DerivePointerAlignment: false +IndentWidth: 4 +ObjCBlockIndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 diff --git a/pfs/.gitignore b/pfs/.gitignore new file mode 100644 index 0000000..873989d --- /dev/null +++ b/pfs/.gitignore @@ -0,0 +1 @@ +custom_makefile_variables diff --git a/pfs/.gitmodules b/pfs/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/pfs/CHANGELOG.md b/pfs/CHANGELOG.md new file mode 100644 index 0000000..892678d --- /dev/null +++ b/pfs/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog +All notable changes to this project will be documented in this file. + +Please refer to README and design documentation under docs folder. +WARNING: The code is not upto the design doc, with some of the +pending work mentioned under Unreleased tag below, and +also specified as TODO(For Implentation) in the design doc. + +## [Unreleased] +- To support additional flags like volume-id and allow-reuse in manifest. +- To support json encoding of volume-meta-data. +- Add support to check if filename, is a valid UTF-8 string. +- Directory-name confidentiality. + +## [0.1] - 2019-07-30 +### Added +- Initial version. Maximum length of filenames for files that require +protection is 180 bytes. + diff --git a/pfs/LICENSE b/pfs/LICENSE new file mode 100644 index 0000000..0d9c8e6 --- /dev/null +++ b/pfs/LICENSE @@ -0,0 +1,35 @@ +LICENSE->Graphene-Filesystem-Shield is released under BSD License +(http://www.opensource.org/licenses/BSD-3-Clause) + +==================COPYRIGHT INFO BELOW============================= + +Copyright (C) 2019 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================= + diff --git a/pfs/LICENSE.addendum b/pfs/LICENSE.addendum new file mode 100644 index 0000000..f704e1b --- /dev/null +++ b/pfs/LICENSE.addendum @@ -0,0 +1,8 @@ +Graphene-Filesystem-shield(GFS) is licensed under BSD. + +GFS also uses the following sources (and licenses): + +Sources from Intel's SGX SDK(in pfs_proxy folder) - BSD +Patched sources for SGX SDK's protected-fs library - BSD +Base64 encoding/decoding - BSD + diff --git a/pfs/Makefile b/pfs/Makefile new file mode 100644 index 0000000..048ce58 --- /dev/null +++ b/pfs/Makefile @@ -0,0 +1,149 @@ +#To enable verbose mode in GCC +#CXXFLAGS +=-v +#To enable which headers it includes +#CXXFLAGS +=-H + +CFLAGS_ASSEMBLY =-DPIC -DSHARED -fPIC -DASSEMBLER -Wa,--noexecstack -x assembler-with-cpp -DIN_ENCLAVE + +#Note: as part of build_deps.sh script, this libpfs_sdk.a, should already be built. +PATH_TO_PFS_SDK_LIB=./pfs_sdk/ + +DEPS_GRAPHENE=./deps/graphene +DEPS_MBEDTLS=./deps/mbedtls +DEPS_LINUX_SGX=./deps/linux-sgx + +#Note: +#By default build script, sets up graphene in this path-> ./deps/graphene +# Path variable DEPS_GRAPHENE can be updated(if needed), +#in a file->custom_makefile_variables, +# so that library(/libfileops_interceptor.so) will get +#copied to user's graphene repo's Runtime directory path. +# Add rule below in file->custom_makefile_variables, if path needs to be updated. +#DEPS_GRAPHENE=/path_to_graphene_repo_directory_path/ +-include ./custom_makefile_variables + +$(info DEPS_GRAPHENE is $(DEPS_GRAPHENE)) + +PATH_TO_GPFS_ROOT=./ +PATH_TO_PROTFS_PROXY=$(PATH_TO_GPFS_ROOT)/pfs_proxy +PATH_TO_GPFS_PRELOAD=$(PATH_TO_GPFS_ROOT)/ld_preload_lib +PROTFS_PROXY_OBJ_DIR=$(PATH_TO_GPFS_ROOT)/pfs_proxy/obj +SGX_PROXY_INC=$(PATH_TO_PROTFS_PROXY)/sgx_proxy/inc +SGX_PROXY_SRC=$(PATH_TO_PROTFS_PROXY)/sgx_proxy/src + +SDK_PROTECTFS=$(PATH_TO_GPFS_ROOT)/pfs_sdk/sdk/protected_fs + +#Related to linux_sgx +EPID_SDK=$(DEPS_LINUX_SGX)/external/epid-sdk-3.0.0 + +SGX_SDK_INCLUDES += -I$(DEPS_LINUX_SGX)/common/inc/internal \ + -I$(EPID_SDK) -I$(DEPS_LINUX_SGX)/common/inc \ + -I/opt/intel/sgxsdk/include \ + -I$(DEPS_LINUX_SGX)//sdk/trts/ \ + -I$(DEPS_LINUX_SGX)//sdk/trts/linux + +CXXFLAGS += -fPIC -Wall -std=gnu++11 \ + -I$(SDK_PROTECTFS)/sgx_uprotected_fs/ \ + -I$(SDK_PROTECTFS)/sgx_tprotected_fs/ \ + -I$(PATH_TO_PROTFS_PROXY) \ + -I$(PATH_TO_GPFS_PRELOAD) \ + -I$(SGX_PROXY_INC) + +CXXFLAGS += $(SGX_SDK_INCLUDES) + +#Related to mbedtls +MBEDTLS_INC=$(DEPS_MBEDTLS)/include +MBEDTLS_SRC=$(DEPS_MBEDTLS)/library + +CXXFLAGS += -I$(MBEDTLS_INC) + +#CFLAGSERRORS=-Wall -Wextra -Wwrite-strings -Wlogical-op -Wshadow -Werror +#Note: Enable Werror to get rid of warnings... +CFLAGSERRORS=-Wall -Wextra -Wwrite-strings -Wlogical-op -Wshadow +CXXFLAGS += $(CFLAGSERRORS)# -DDEBUG -DDYNAMIC_RSA + +CFLAGS := $(filter-out -std=gnu++11,$(CXXFLAGS)) + +CFLAGS += -std=gnu99 + +$(info CXXFLAGS is $(CXXFLAGS)) +$(info CFLAGS is $(CFLAGS)) +$(info PROTFS_PROXY_OBJ_DIR is $(PROTFS_PROXY_OBJ_DIR)) +$(info CXX is $(CXX)) +$(info CC is $(CC)) + +#Using makefile's implicit rules to compile *.c files into *.o object files, using CFLAGS. +#https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html +MBEDTLS_CRYPTO_OBJS := $(MBEDTLS_SRC)/gcm.o $(MBEDTLS_SRC)/aes.o $(MBEDTLS_SRC)/cmac.o \ + $(MBEDTLS_SRC)/cipher.o $(MBEDTLS_SRC)/cipher_wrap.o $(MBEDTLS_SRC)/aesni.o \ + $(MBEDTLS_SRC)/ccm.o $(MBEDTLS_SRC)/sha256.o $(MBEDTLS_SRC)/md.o \ + $(MBEDTLS_SRC)/md_wrap.o $(MBEDTLS_SRC)/md5.o $(MBEDTLS_SRC)/sha1.o \ + $(MBEDTLS_SRC)/ripemd160.o $(MBEDTLS_SRC)/sha512.o $(MBEDTLS_SRC)/platform_util.o + +$(MBEDTLS_SRC)/libmbedtls_crypto.a: $(MBEDTLS_CRYPTO_OBJS) + $(AR) rcs $@ $^ + +PROTFS_SGX_PROXY_SRC_FILES := $(wildcard $(SGX_PROXY_SRC)/*.cpp) +PROTFS_SGX_PROXY_OBJ_FILES := $(patsubst $(SGX_PROXY_SRC)/%.cpp,$(PROTFS_PROXY_OBJ_DIR)/%.o,$(PROTFS_SGX_PROXY_SRC_FILES)) + +$(PROTFS_PROXY_OBJ_DIR)/%.o: $(SGX_PROXY_SRC)/%.cpp + mkdir -p $(PROTFS_PROXY_OBJ_DIR) + g++ -c $(CXXFLAGS) $< -o $@ + +PROTFS_SRC_FILES := $(wildcard $(PATH_TO_PROTFS_PROXY)/*.cpp) +PROTFS_OBJ_FILES := $(patsubst $(PATH_TO_PROTFS_PROXY)/%.cpp,$(PROTFS_PROXY_OBJ_DIR)/%.o,$(PROTFS_SRC_FILES)) + +$(PROTFS_PROXY_OBJ_DIR)/%.o: $(PATH_TO_PROTFS_PROXY)/%.cpp + g++ -c $(CXXFLAGS) $< -o $@ + +PROTFS_ASSEMBLY_SRC_FILES := $(wildcard $(SGX_PROXY_SRC)/*.S) +PROTFS_ASSEMBLY_OBJ_FILES := $(patsubst $(SGX_PROXY_SRC)/%.S,$(PROTFS_PROXY_OBJ_DIR)/%.o,$(PROTFS_ASSEMBLY_SRC_FILES)) + +CFLAGS_ASSEMBLY += $(SGX_SDK_INCLUDES) + +$(PROTFS_PROXY_OBJ_DIR)/%.o: $(SGX_PROXY_SRC)/%.S + gcc -c $(CFLAGS_ASSEMBLY) $^ -o $@ + +$(PATH_TO_PROTFS_PROXY)/libpfs_proxy.a: $(PROTFS_SGX_PROXY_OBJ_FILES) \ + $(PROTFS_OBJ_FILES) $(PROTFS_ASSEMBLY_OBJ_FILES) -ldl + $(AR) rcs $@ $^ + +$(PATH_TO_PFS_SDK_LIB)/libpfs_sdk.a: + cd $(PATH_TO_PFS_SDK_LIB) && make -fMakefile_pfs_sdk all + +FILEOPS_INTERCEPTOR_FILES := $(wildcard $(PATH_TO_GPFS_PRELOAD)/*.c) +$(info FILEOPS_INTERCEPTOR_FILES is $(FILEOPS_INTERCEPTOR_FILES)) + +$(PATH_TO_GPFS_PRELOAD)/libfileops_interceptor.so : $(FILEOPS_INTERCEPTOR_FILES) $(PATH_TO_PROTFS_PROXY)/libpfs_proxy.a $(MBEDTLS_SRC)/libmbedtls_crypto.a $(PATH_TO_PFS_SDK_LIB)/libpfs_sdk.a + g++ -o $@ $^ -g -std=c++11 -shared -fPIC $(CXXFLAGS) -L$(PATH_TO_PROTFS_PROXY) -L$(MBEDTLS_SRC) -L$(PATH_TO_PFS_SDK_LIB) -lpfs_proxy -lpfs_sdk -lmbedtls_crypto -ldl + cp $@ $(DEPS_GRAPHENE)/Runtime + +README.html : README.md + pandoc --from markdown_github --to html --standalone $< --output $@ + +clean: +#Not cleaning external libraries(that we dont modify) under +#/deps/ in build_deps.sh. + rm -rf ./ld_preload_lib/libfileops_interceptor.so + rm -rf ./pfs_proxy/libpfs_proxy.a + rm -rf $(MBEDTLS_SRC)/libmbedtls_crypto.a $(MBEDTLS_SRC)/*.o + rm -rf $(SDK_PROTECTFS)/obj; + cd $(PATH_TO_PFS_SDK_LIB) && make -fMakefile_pfs_sdk clean + +all: format $(PATH_TO_GPFS_PRELOAD)/libfileops_interceptor.so + +.PHONY: format +format: + clang-format -i $(shell find . -path ./deps -prune -o \ + -path ./pfs_sdk -prune -o \ + \( -name '*.h' -o -name '*.cpp' -o -name '*.c' \) -print) + #clang-format -i $(shell find ./pfs_sdk/sdk/protected_fs \ + # \( -name '*.h' -o -name '*.cpp' -o -name '*.c' \) -print) + +mrproper: clean + $(RM) -r deps/graphene deps/linux-sgx deps/linux-sgx-driver + $(RM) -r deps/mbedtls + rm -rf ./pfs_sdk/.git + rm -rf ./pfs_sdk/sdk + +#.PHONY = all mrproper diff --git a/pfs/README.md b/pfs/README.md new file mode 100644 index 0000000..d799e7f --- /dev/null +++ b/pfs/README.md @@ -0,0 +1,255 @@ +**Protected-FileSystem(PFS) shield library:** + +These sources produce a library, that provides file-system shield to [Graphene](https://github.com/oscarlab/graphene), by incorporating file protection feature from
+Intel's [SGX-SDK's Protected-FS library](https://github.com/intel/linux-sgx/tree/master/sdk/protected_fs).
+For sake of brevity, this feature or library will hereon be referred to as PFS.
+ +**ASSUMPTION/PRE-REQUISTE:** +* User of this PFS library, is expected to be familiar with [Graphene-SGX](https://github.com/oscarlab/graphene/wiki/Introduction-to-Intel-SGX-Support).
+* User uses this PFS library, with Intel SGX enabled in Graphene.
+* User is also expected to be familiar with building applications using Graphene.
+* This PFS library will reside as an optional plugin at the top-level directory in Graphene repo.
+* User has familiarity with Intel's [SGX feature sources](https://github.com/intel/linux-sgx)
+* User has familiarity with Intel [SGX SDK's Protect-FS feature](https://software.intel.com/en-us/articles/overview-of-intel-protected-file-system-library-using-software-guard-extensions) + +**DETAILED OVERVIEW:** +Integrated [SGX SDK's Protected-FS](https://github.com/intel/linux-sgx/tree/master/sdk/protected_fs) library WITHOUT sgx-sdk dependencies, as a library that can be linked by unmodified +applications in [Graphene](https://github.com/oscarlab/graphene)\ +*Note: Sgx-sdk dependencies for crypto, getting EREPORT, EGETKEY, have been substituted by taking some of the related +code from SGX-SDK, and packaging as part of this PFS library* + +Overloaded library called as libfileops_interceptor.so(i.e PFS library), has been added,which can route the file-related C api calls to corresponding api in SGX-SDK's Protected-FS library\ +(OR) regular glibc's file-system api\ +*Graphene application’s manfiest file, specifies the path to directory where protected files reside.
* +For "List of Filesystem apis handled (and the ones not handled) by library", please refer to section below in README:
+ +Using the overloaded library(libfileops_interceptor.so), we are able to transparently intercept file-system api calls
+from unmodified application, and provide cryptographic protection mechanism(using SGX-ProtectedFS library).
+
+Files to be protected are mounted on pre-defined directory-path specified in graphene application's manifest.
+With SGX-SDK's Protected-FS mentioned above we only get confidentiality and integrity for fileÂ’s content, and integrity for filename.
+
+Additionally, with PFS library, we are able to add security mechanism for filename confidentiality, filename integrity, file-path integrity and\ +binding of files to a volume, on top of existing filesystem protection software (i.e. SGX-SDK's Protected-FS sources).
+
+To summarise, PFS provides filesystem shield feature in graphene, so that it extends Graphene(a system which allows existing applications to run inside SGX), with transparent protection of file I/O.
+More specifically, it transparently intercepts all file I/O calls and, if the implied filename is below some pre-defined mount-points, cryptographically protects
+the corresponding operations while interacting with the untrusted filesystem + +For more details refer to [design document](docs/design.md). + +**List of directories and components added:** +1. [docs](docs) folder has design document. **Note: Code is NOT fully upto the design document, CHANGELOG has pending features, +and you can also refer to tag->"TODO(For Implementation)" specified in design doc for additional features yet-to-be-implemented.
** +With PFS library, the features that have been implemented are: filename confidentiality, filename integrity, file-path integrity and binding of files to a volume,
+on top of existing filesystem protection software (i.e. SGX-SDK's Protected-FS).
+2. [ld_preload_lib](ld_preload_lib): Has sources to intercept and overload file-system apis, and route it to SGX-SDK's Protected-FS library or to C filesystem api. Also has sources to augment cryptographic mechanisms +to add additional features as described in the design doc under docs directory.
+3. [pfs_sdk](pfs_sdk): Has a patch file that gets applied on SGX-SDK's Protected-FS library, to make it work within graphene, without sgx-sdk dependencies.
+4. [pfs_proxy](pfs_proxy): Has changes, to hook calls from SGX-SDK's Protected-FS library sources, to non-sgx-sdk apis.
+5. [apps/pfs_app](apps/pfs_app): Has sources for sample graphene application, that sets the following in the
+application's manifest(specifies path to proteced directory, sets the overloaded PFS library, sets the type of key to be used for file-protection), and uses PFS library\ +transparently as a file-system shield + +**Maximum length of filenames allowed for protected files:** +Given that protected files' names are encrypted and then base64 encoded, +we have a maximum limit of 180 bytes for the length of filename of a protected file. +The [design document](docs/design.md), discusses about base64 encoding of filenames for protected files. + +**Setting parameters in Graphene application's manifest:**
+*Note: Refer to [README](apps/pfs/README.md) under [apps/pfs_app](apps/pfs_app) for details on how to set various parameters in application's manifest* +* Briefly, application's manifest has fields to set the overloaded library(libfileops_interceptor.so), + directory-path where protected-files are mounted, and type of key(SGX sealing key or custom-key) is + used to protect the files.
+*Note: High-level steps for how to generate sealed_custom_key file and process it is mentioned below.*
+*Note: Although [design document](docs/design.md) specifies volume-id setting in manifest, this is currently not supported yet.*
+ +**Build steps:** +1. Installing dependencies(under ./deps) for the build.
+./build_deps.sh
+Note: Above script->build_deps.sh will prompt user to install a version of Graphene,
+under ./deps. Installation of graphene can be skipped by the user, if user already has Graphene
+installed in a different directory path.
+ +2. Building the PFS library:
+If user has Graphene installed in a different directory path(default path is ./deps/graphene):
+i. Add a file->custom_makefile_variables, with path set to your graphene repo.
+ii. Add rule below in file->custom_makefile_variables, with the directory-path.
+iii. DEPS_GRAPHENE=/path_to_graphene_repo_directory_path/
+and then run the script below:
+./build_library.sh
+Above script will copy the library(libfileops_interceptor.so)
+into the Runtime directory of graphene.
+ +3. Building sample application at apps/pfs_app:
+cd apps/pfs_app
+User needs to update pfs_application's manifest file with the path of Graphene and other configuration parameters.
+For configuration setup, application build steps, and for details on how to set various parameters in application's manifest,
+refer to [README](apps/pfs_app/README.md) under [apps/pfs_app](apps/pfs_app)
+ +**Kernel Driver Dependencies:** +1. Need to make sure that both SGX driver and graphene SGX driver are loaded to run graphene.
+2. [SGX Driver installation](https://github.com/intel/linux-sgx-driver) steps.
+3. [Graphene SGX Driver installation](https://github.com/oscarlab/graphene/wiki/SGX-Quick-Start) steps
+4. When you do lsmod | grep sgx, you would have to see the following drivers loaded:
+graphene_sgx
+isgx
+ + +**Steps for how to generate sealed_custom_key file:**
+**Pre-requistes generate sealed_custom_key file:**
+Custom key can be generated on a SGX system(Factory system A) within trusted premises(like a factory-like enviornment).
+Custom key needs to sent via SGX remote attestation to the target SGX system(Target system B).
+Note: These are high-level steps:
+1. SGX enclave(Enclave A) running on system A needs to generate random custom key and +send custom key to SGX enclave(Enclave B) on system B, through remote attestation. +2. Once Enclave B(running on Target system B) receives the custom key via SGX remote attestation, it needs to +invoke SGX sealing api, to seal the custom-key and output the sealed blob to file-system on Target system B. + +**Steps for how to process existing sealed_custom_key file:**
+**Pre-requisite: sealed-custom key, is generated and exists in filesystem as explained above.:**
+1. If PFS_USE_CUSTOM_KEY is set to 'yes' in application's manifest, this library(fops_interceptor.so), will expect a sealed_custom_key, in the current working directory\ +of the application.
+2. If present, it will unseal the blob, and save the custom-key in graphene application's memory(application runs as SGX enclave) +3. [Design document](docs/design.md) descibes how custom file-content protection key and other keys are derived from this custom-key. +3. So for any C filesystem api calls that application invokes like fopen/fread/fwrite and so on, custom file-content protection key(derived +from custom-key) will be used to protect the contents of the files. + +**List of Filesystem apis handled (and the ones not handled) by library:**
+**Note: For non-protected files, ALL C filesystem apis are supported.**
+The list below for what is supported/not-supported, only applies
+to protected files. + +**List of C filesystem apis(returns or uses FILE *) supported by library:**
+**SUPPORTED: For the apis below, library will intercept and route it to SGX-ProtectedFS library api:** +* fopen +* fflush +* fclose +* fread +* fwrite +* fgetc +* getc +* fgets +* fputc +* putc +* fputs +* ungetc +* ftell +* ftello +* fseek +* fseeko +* rewind +* clearerr +* ferror +* feof +* remove + +**C filesystem apis(file-descriptor based apis) NOT supported by library:** +Note: Un-supported apis can still be used for files that do NOT need protection. For any calls to un-supported apis,
+if the path matches with the protected_directory specified in the manifest, then PFS library, will NOT allow those calls,
+and will return an error.
* + +**Apis that take file-descriptor are NOT supported. APIs that create new file-descriptor like
+open/openat/creat are NOT supported. Library will intercept and return error:**
+* open +* openat +* creat(and their 64-bit versions). + +*Since open/openat/creat are NOT supported for protected-files, any api that passes fd(file-descriptor) is NOT intercepted by the library.
+Since those system calls(using file-descriptor) will be for non-protected files, it will be directly handled by underlying C library
* + +****For the un-supported (returns or uses FILE *) apis below, library will intercept and return error gracefully.:**** +* freopen +* truncate +* setbuf +* setbuffer +* setlinebuf +* setvbuf +* vfscanf +* vfprintf +* fgetpos +* fsetpos +* fileno +* fscanf +* fprintf + +**File-rename apis NOT supported, intercepted by library, and returns error:** +* rename +* renameat + +**Wide-character FILE * apis, NOT supported, intercepted by library, and returns error:** +* fwide +* fgetwc +* getwc +* fgetws +* fputwc +* putwc +* fputws +* ungetwc +* vfwscanf +* vfwprintf +* fwscanf +* fwprintf + +**List of 64-bit Filesystem apis handled (and the ones not handled) by library:**
+Note: For 32-bit systems, C lib provides 64 bit versions of certain apis, mainly related
+to file-open/seek/tell to support LFS(Large-File-system) on 32-bit systems.
+The program should enable this conditional->_FILE_OFFSET_BITS == 64, to use 64-bit apis(and types)
+on 32-bit systems. On 64-bit systems, no such conditional is needed , by default apis(and types)
+are 64-bit compliant. For example, calling fseek on 64 bit-system is equivalent to calling fseek64 api.**
+****SUPPORTED: List of 64-bit C filesystem apis(returns or uses FILE *) supported by library:**** +* fopen64 +* ftello64 +* fseeko64 + +**64-bit C filesystem apis(file-descriptor based apis) NOT supported by library. Library will intercept and return error::** +* open64 +* openat64 +* creat64 + +****For the 64-bit un-supported (returns or uses FILE *) apis below, library will intercept and return error gracefully.:**** +* freopen64 +* fgetpos64 +* fsetpos64 + + +**List of directory-system handled (and the ones not handled) by library:** +**Note: For non-protected files, ALL C directory-system apis are supported.**
+The list below for what is supported/not-supported, only applies
+to directory-paths(where protected files reside), as set
+in application's manifest.
+ +**SUPPORTED: List of directory-system apis supported by library:** +* opendir +* closedir +* readdir +* readdir64 +* rewindir +* seekdir +* telldir + +**List of directory-system apis NOT-supported, intercepted by library, and returns error:** +* readdir_r +* readdir64_r +* dirfd +* scandir +* scandir64 +* scandirat +* scandirat64 +* nftw +* nftw64 + +**NO interception/handling for apis below. Since open/openat/creat are NOT supported for directory-paths
+where protected-files reside**
+System calls(using file-descriptor) will be for directory-paths to NON-protected files, and it will +be directly handled by underlying C library.:
+* fdopendir +* getdirentries +* getdirentries64 + +**NO interception/handling for apis below. Not feasible to +intercept and handle these apis.:**
+* alphasort +* alphasort64 +* versionsort +* versionsort64 diff --git a/pfs/apps/pfs_app/Makefile b/pfs/apps/pfs_app/Makefile new file mode 100644 index 0000000..a6c7d87 --- /dev/null +++ b/pfs/apps/pfs_app/Makefile @@ -0,0 +1,100 @@ + +cxx_srcs = $(patsubst %.cpp,%.cpp,$(wildcard *.cpp)) + +GRAPHENE_PATH = ../../deps/graphene +SHIMDIR = $(GRAPHENE_PATH)/LibOS/shim/src + +$(info GRAPHENE_PATH is $(GRAPHENE_PATH)) +$(info SHIMDIR is $(SHIMDIR)) + +#To enable verbose mode in GCC +CXXFLAGS +=-v +#To enable which headers it includes +#CXXFLAGS +=-H + +PATH_TO_PFS_SDK_LIB=$(PATH_TO_PFS_ROOT)/pfs_sdk/ + +#TODO: Needs to be updated as needed +PATH_TO_PFS_ROOT=../../ +PATH_TO_PFS_PROXY=$(PATH_TO_PFS_ROOT)/pfs_proxy +PATH_TO_PFS_PRELOAD=$(PATH_TO_PFS_ROOT)/ld_preload_lib +PFS_PROXY_OBJ_DIR=$(PATH_TO_PFS_ROOT)/pfs_proxy/obj +SGX_PROXY_INC=$(PATH_TO_PFS_PROXY)/sgx_proxy/inc +SGX_PROXY_SRC=$(PATH_TO_PFS_PROXY)/sgx_proxy/src + +SDK_PROTECTFS=$(PATH_TO_PFS_ROOT)/pfs_sdk/sdk/protected_fs + +SGX_GIT=$(PATH_TO_PFS_ROOT)/deps/linux-sgx +EPID_SDK=$(SGX_GIT)/external/epid-sdk-3.0.0 + +MBEDTLS=$(PATH_TO_PFS_ROOT)/deps/mbedtls +MBEDTLS_INC=$(MBEDTLS)/include +MBEDTLS_SRC=$(MBEDTLS)/library + +SGX_SDK_INCLUDES += -I$(SGX_GIT)/common/inc/internal \ + -I$(EPID_SDK) -I$(SGX_GIT)/common/inc \ + -I/opt/intel/sgxsdk/include + +CXXFLAGS += -fPIC -Wall -std=gnu++11 \ + -I$(SDK_PROTECTFS)/sgx_uprotected_fs/ \ + -I$(SDK_PROTECTFS)/sgx_tprotected_fs/ \ + -I$(PATH_TO_PFS_PROXY) \ + -I$(PATH_TO_PFS_PRELOAD) \ + -I$(SGX_PROXY_INC) + +CXXFLAGS += $(SGX_SDK_INCLUDES) + +CXXFLAGS += -I. -Ideps/local/include +#CFLAGSERRORS=-Werror -Wall -Wextra -Wwrite-strings -Wlogical-op -Wshadow +CFLAGSERRORS=-Wall -Wextra -Wwrite-strings -Wlogical-op -Wshadow +CXXFLAGS += $(CFLAGSERRORS)# -DDEBUG -DDYNAMIC_RSA + +#Note: Below flags needed only to test +#64 bit apis(explicitly like on 32-bit systems). +#On 64-bit systems, this conditional NOT needed. +#Enabling below conditional...replaces calls to 64-bit +#equivalent apis..like readdir to readdir64. +#CXXFLAGS += -D_FILE_OFFSET_BITS=64 + +CFLAGS := $(filter-out -std=gnu++11,$(CXXFLAGS)) + +CFLAGS += -std=gnu99 + + +CFLAGS += -I$(MBEDTLS_INC) + +$(info CXXFLAGS is $(CXXFLAGS)) +$(info CFLAGS is $(CFLAGS)) +$(info PFS_PROXY_OBJ_DIR is $(PFS_PROXY_OBJ_DIR)) + +#For debugging symbols. +CXXFLAGS +=-g + +CXXFLAGS += -I$(SHIMDIR)/../../../Pal/src/host/Linux-SGX/ \ + -I$(SHIMDIR)/../../../Pal/include/pal \ + -I$(MBEDTLS_INC) + #-I$(SHIMDIR)/../../../Pal/lib/crypto/mbedtls/mbedtls/ + +$(info CXXFLAGS is $(CXXFLAGS)) + + +pfs_app: + $(info cxx_srcs is $(cxx_srcs)) + g++ -o pfs_app $(cxx_srcs) $(CXXFLAGS) -L$(PATH_TO_PFS_PROXY) -L$(MBEDTLS_SRC) -L$(PATH_TO_PFS_SDK_LIB) -lpfs_proxy -lpfs_sdk -lmbedtls_crypto -ldl + $(GRAPHENE_PATH)/Pal/src/host/Linux-SGX/signer/pal-sgx-sign -libpal $(GRAPHENE_PATH)/Runtime/libpal-Linux-SGX.so -key $(GRAPHENE_PATH)/Pal/src/host/Linux-SGX/signer/enclave-key.pem -output $@.manifest.sgx -exec $@ -manifest pfs_app.manifest.template + $(GRAPHENE_PATH)/Pal/src/host/Linux-SGX/signer/pal-sgx-get-token -output $@.token -sig $@.sig + +#mbedtls-ssl-server : $(MBEDTLS_SSL_SERVER_SRC) ssl-server.manifest deps/graphene/Runtime/pal_loader +# $(CC) $(MBEDTLS_SSL_SERVER_SRC) -o $@ $(CFLAGSERRORS) $(SSL_SERVER_INCLUDES) -Ldeps/local/lib/ $(MBEDTLS_SSL_SERVER_LIBS) +# deps/graphene/Pal/src/host/Linux-SGX/signer/pal-sgx-sign -libpal deps/graphene/Runtime/libpal-Linux-SGX.so -key deps/graphene/Pal/src/host/Linux-SGX/signer/enclave-key.pem -output $@.manifest.sgx -#exec $@ -manifest ssl-server.manifest +# deps/graphene/Pal/src/host/Linux-SGX/signer/pal-sgx-get-token -output $@.token -sig $@.sig + + +.PHONY: format +format: + clang-format -i $(shell find . \( -name '*.h' -o -name '*.cpp' -o -name '*.c' \) -print) + +all: pfs_app + +clean: + $(RM) pfs_app pfs_app.token pfs_app.sig pfs_app.manifest.sgx diff --git a/pfs/apps/pfs_app/README.md b/pfs/apps/pfs_app/README.md new file mode 100644 index 0000000..be091a4 --- /dev/null +++ b/pfs/apps/pfs_app/README.md @@ -0,0 +1,83 @@ +**Pre-requiste:** +Please refer to README.md at the top-level directory, to get an overview on Protected-Filesystem(PFS) shield library, +and the build and setup steps, before reading below. +For sake of brevity, this feature or library will hereon be referred to as PFS. + +PFS library(libfileops_interceptor.so) will be used by pfs application, to transparently intercept filesystem I/O apis, and thereby +provide filesystem shield, and internally invoke apis in file protection feature from +Intel's [SGX-SDK's Protected-FS](https://github.com/intel/linux-sgx/tree/master/sdk/protected_fs) library. + +**SETUP PRIOR TO RUNNING THE PROTFS_APP:**
+**NOTE:** The application's manifest file(pfs_app.manifest.template), needs some modifications(as explained below), in order for it work as expected.
+ +* application's manifest should be set to point to the Runtime folder in graphene repo.
+For example, in the example manifest file, path is->../../deps/graphene/Runtime, it needs to be changed as per user's path to graphene repo.
+* User needs to setup physical directory path(on user's system) to a mount-point, so graphene can mount(i.e. map) the physical
+directory path to a virtual directory path in in its internal namespace.
+Update below in the application manifest file(user needs to change as per user's setup):
+Physical directory path is->/home/skris14/pfs_mount
+Virtual directory path is ->/pfs_dir
+* Setting the overloaded PFS library:
+*loader.env.LD_PRELOAD = libfileops_interceptor.so
* +* Mapping the physical directory path to virtual directory path in manifest:
+Update below in the application manifest file(user needs to change as per user's setup):
+*fs.mount.pfs.type = chroot
+fs.mount.pfs.path = /pfs_dir
+fs.mount.pfs.uri = file:/home/skris14/pfs_mount
* +* User needs to be setup the following directory structure under the physical directory path(like below):
+cd to mount_point, and then run below:
+mkdir -p test_dir secrets/sub_dir1 secrets/sub_dir2 non_secrets
+ +After creating above sub-directories, doing ls at mount-point, should list below:
+~/pfs_mount$ ls -ltr
+test_dir
+secrets
+non_secrets
+~/pfs_mount$ ls -ltr secrets/
+sub_dir2
+sub_dir1
+ +* Setting root of protected directory(PFS_MOUNT_POINT):
+**Note: PFS_MOUNT_POINT points to the top-level directory that can have protected files.**
+Update below in the application manifest file(user needs to change as per user's setup):
+loader.env.PFS_MOUNT_POINT = /pfs_dir/secrets
* +Note that /pfs_dir is virtual directory-path. Physical directory-path has been mapped +to this virtual path in graphene's namespace, as explained earlier. +In this example, PFS_MOUNT_POINT happens to be a sub-directory of the physical directory that has been mounted +and mapped to virtual directory path as per rules in graphene's manifest file. +* User needs to allow access to files under a given directory:
+Update below in the application manifest file(user needs to change as per user's setup):
+*sgx.allowed_files.mount_path = file:/home/skris14/pfs_mount
* +**Note: In this example, we have allowed access to files in the parent directory +of PFS_MOUNT_POINT. Reason being this application accesses some of the non-protected directories using the virtual directory path.**
+ +* Setting file-protection key-type:
+Set PFS_USE_CUSTOM_KEY, in application's manifest, to indicate whether PFS will be using custom key or autokey(i.e sealing key)
+Update below in the application manifest file(user needs to change as per user's setup):
+*loader.env.PFS_USE_CUSTOM_KEY = no
* +If PFS_USE_CUSTOM_KEY is set to 'no' in application's manifest, PFS, will use SGX sealing key for protecting files.
+Design doc under docs folder descibes how keys for filename encryption and other keys are derived from SGX sealing-key.
+If PFS_USE_CUSTOM_KEY is set to 'yes' in application's manifest, this library(fops_interceptor.so), will expect a sealed_custom_key, in the current working directory
+of the application.
+***Note: High-level steps for how to generate sealed_custom_key file and process it is mentioned in README under the top-level directory.*
*** + +**BUILDING AND RUNNING THE APP:**
+1. Option1: If PFS is already built, and just to build app and run
+*./build_app.sh
+./run_app.sh
* +2. Option2: To re-build PFS library, and then build app and run
+*./build_library_app_run.sh
* + +**PROTFS_APP DETAILS:**
+ 1. pfs_override_test(PATH_TO_PFS_TESTFILE) API:
+ Creates and/or updates a file. Note that it just calls file-system apis, like fopen, fread and so on, + but the overloaded PFS library(libfileops_interceptor.so), intercepts those calls, and depending on the path specified in the manifest, + it can either protect the file, by calling SGX SDK's protected-fs apis or route it to regular system call.
+ 2. protect_files(CLEAR_DIR_PATH, PROTECTED_DIR_PATH) API:
+ This API can be used to take existing files in clear(non-encrypted) and output protected files. + It expects path to directory that has clear files(CLEAR_DIR_PATH), and outputs protected files + to protected directory(PROTECTED_DIR_PATH, which should be same as sub-directory of PFS_MOUNT_POINT + in manifest). + Additionally if CLEAR_DIR_PATH has sub-directories, it can replicate the sub-directories in + PROTECTED_DIR_PATH. To summarise, it can recursively traverse the directory-tree under CLEAR_DIR_PATH + and create sub-directories(if needed) in PROTECTED_DIR_PATH and output protected files. diff --git a/pfs/apps/pfs_app/build_app.sh b/pfs/apps/pfs_app/build_app.sh new file mode 100755 index 0000000..29a8713 --- /dev/null +++ b/pfs/apps/pfs_app/build_app.sh @@ -0,0 +1,3 @@ +make clean + +make all diff --git a/pfs/apps/pfs_app/build_library_app_run.sh b/pfs/apps/pfs_app/build_library_app_run.sh new file mode 100755 index 0000000..98be86f --- /dev/null +++ b/pfs/apps/pfs_app/build_library_app_run.sh @@ -0,0 +1 @@ +cd ../../; ./build_library.sh ; cd - ; make format; sudo ./build_app.sh; sudo ./run_app.sh diff --git a/pfs/apps/pfs_app/pal_loader b/pfs/apps/pfs_app/pal_loader new file mode 120000 index 0000000..f0a9d2e --- /dev/null +++ b/pfs/apps/pfs_app/pal_loader @@ -0,0 +1 @@ +../../deps/graphene/Runtime/pal_loader \ No newline at end of file diff --git a/pfs/apps/pfs_app/perf_meas.cpp b/pfs/apps/pfs_app/perf_meas.cpp new file mode 100644 index 0000000..d2ae629 --- /dev/null +++ b/pfs/apps/pfs_app/perf_meas.cpp @@ -0,0 +1,244 @@ +/* + * License: BSD 3-Clause "New" or "Revised" License + * + * Other web pages for this license + * http://www.opensource.org/licenses/BSD-3-Clause + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "perf_meas.h" + +#include +#include + +#include "pfs_app.h" + +float perf_meas[MAX_APIS]; +// Note: Order of strings needs to match, with enum->apis_measured above. +char apis_meas[MAX_APIS][255] = {"fopen", "fread", "fwrite", "fflush", "fseek", "ftell", "fclose"}; + +uint8_t onek_buf[1024] = {1}; +uint8_t twok_buf[2048] = {2}; +uint8_t fourk_buf[4096] = {4}; +uint8_t eightk_buf[8192] = {8}; + +struct perf_run runs[] = {{onek_buf, sizeof(onek_buf)}, + {twok_buf, sizeof(twok_buf)}, + {fourk_buf, sizeof(fourk_buf)}, + {eightk_buf, sizeof(eightk_buf)}}; + +void get_time(struct timeval* time) { + if (!time) + return; + + gettimeofday(time, NULL); +} + +void compute_duration(struct timeval* t1, struct timeval* t2, float* elapsed) { + if (!t1 || !t2 || !elapsed) + return; + + *elapsed = t2->tv_sec - t1->tv_sec; + *elapsed += (t2->tv_usec - t1->tv_usec) / 1e6; +} + +void get_time_and_compute_duration(struct timeval* t1, struct timeval* t2, float* elapsed) { + if (!t1 || !t2 || !elapsed) + return; + + get_time(t2); + + compute_duration(t1, t2, elapsed); +} + +void print_perf_meas(size_t buf_len, bool pfs_file) { + int i = 0; + + printf("\nperf meas for file_type=%s, buf_size=%lu\n", + ((pfs_file) ? "PFS_FILE" : "NON_PFS_FILE"), buf_len); + + for (i = 0; i < MAX_APIS; i++) { + printf("%s, %f\n", apis_meas[i], perf_meas[i]); + } +} + +int pfs_perf_meas_test(const char* path_to_file, uint8_t* input_buf, size_t buf_len) { + FILE* sfp = NULL; + size_t buffer_len = 0; + int32_t ret_val = 0; + struct timeval t1, t2; + float elapsed; + + if (!path_to_file || !input_buf || (buf_len == 0)) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + printf("\nsizeof(path)=%lu, file-name=%s", strlen(path_to_file), path_to_file); + + get_time(&t1); + sfp = fopen((const char*)path_to_file, "rb+"); + + if (sfp != NULL) + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FOPEN] = elapsed; + + printf("\nDEBUG, %s:%d, after fopen to check if file exists, sfp=%p\n", __func__, __LINE__, + sfp); + + if (sfp == NULL) { + printf("\nDEBUG, creating new testfile in wb+ mode \n"); + + get_time(&t1); + sfp = fopen((const char*)path_to_file, "w+b"); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FOPEN] = elapsed; + } else { + fclose(sfp); + printf("\nDEBUG, opening existing file-%s in ab+ mode\n", path_to_file); + + sfp = fopen((const char*)path_to_file, "a+b"); + } + + printf("\nDEBUG, %s:%d, after open, sfp=%p\n", __func__, __LINE__, sfp); + + if (!sfp) { + printf("\nDEBUG, %s:%d, error sfp=%p is NULL\n", __func__, __LINE__, sfp); + return -1; + } + + get_time(&t1); + buffer_len = fwrite(input_buf, sizeof(uint8_t), buf_len, sfp); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FWRITE] = elapsed; + printf("DEBUG, after fwrite, buffer_len = %d\n", (int)buffer_len); + + /*Note: skipping fflush here, and trying to measure..potential + flush from cache..during fclose + get_time(&t1); + ret_val = fflush(sfp); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FFLUSH] = elapsed; + printf("DEBUG, after fflush, ret_val = %d\n", (int)ret_val); + */ + + ret_val = ferror(sfp); + printf("DEBUG, after ferror, ret_val = %d\n", (int)ret_val); + + memset(input_buf, 0, buf_len); + + get_time(&t1); + ret_val = fseek(sfp, 0L, SEEK_SET); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FSEEK] = elapsed; + printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + get_time(&t1); + buffer_len = fread(input_buf, sizeof(uint8_t), buf_len, sfp); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FREAD] = elapsed; + printf("\nDEBUG, after fread, buffer_len = %d\n", (int)buffer_len); + + // Read from File + uint64_t file_offset = 0; + get_time(&t1); + file_offset = ftell(sfp); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FTELL] = elapsed; + printf("\nDEBUG: File offset = %lu\n", file_offset); + + ret_val = feof(sfp); + printf("DEBUG, after feof, ret_val = %d\n", (int)ret_val); + + clearerr(sfp); + printf("DEBUG, after clearerr\n"); + + ret_val = feof(sfp); + printf("DEBUG, after feof, ret_val = %d\n", (int)ret_val); + + get_time(&t1); + ret_val = fclose(sfp); + get_time_and_compute_duration(&t1, &t2, &elapsed); + perf_meas[FCLOSE] = elapsed; + printf("DEBUG, after fclose, ret_val = %d\n", (int)ret_val); + + // ret_val = remove(path_to_file); + // printf("DEBUG, after remove, ret_val = %d\n", (int)ret_val); + + return ret_val; +} + +void pfs_run_perf_meas_test(const char* path_to_file) { + struct timeval t1, t2; + float elapsed; + unsigned int i; + + printf("%s: %d\n", __func__, __LINE__); + + if (!path_to_file) + return; + + printf("runs=%lu, perf=%lu, val=%lu\n", sizeof(runs), sizeof(struct perf_run), + sizeof(runs) / sizeof(struct perf_run)); + + for (i = 0; i < sizeof(runs) / sizeof(struct perf_run); i++) { + get_time(&t1); + pfs_perf_meas_test(path_to_file, runs[i].ptr_to_buf, runs[i].buf_len); + get_time_and_compute_duration(&t1, &t2, &elapsed); + printf( + "time taken by pfs_perf_meas_test for NON_PFS: %f sec, " + "buf_size=%lu\n", + elapsed, runs[i].buf_len); + print_perf_meas(runs[i].buf_len, 0); + } + + for (i = 0; i < sizeof(runs) / sizeof(struct perf_run); i++) { + get_time(&t1); + pfs_perf_meas_test(path_to_file, runs[i].ptr_to_buf, runs[i].buf_len); + get_time_and_compute_duration(&t1, &t2, &elapsed); + printf( + "time taken by pfs_perf_meas_test for PFS: %f sec, " + "buf_size=%lu\n", + elapsed, runs[i].buf_len); + print_perf_meas(runs[i].buf_len, 1); + } + + return; +} diff --git a/pfs/apps/pfs_app/perf_meas.h b/pfs/apps/pfs_app/perf_meas.h new file mode 100644 index 0000000..079780a --- /dev/null +++ b/pfs/apps/pfs_app/perf_meas.h @@ -0,0 +1,64 @@ +/* + * License: BSD 3-Clause "New" or "Revised" License + * + * Other web pages for this license + * http://www.opensource.org/licenses/BSD-3-Clause + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _PERF_MEAS_H_ +#define _PERF_MEAS_H_ + +#include +#include "stddef.h" +#include "stdint.h" +#include "stdio.h" + +/* TODO: although we can get perf measurements for + individual apis, for varying buffer sizes for fread/fwrite etc... + depending on the current file size...the time for fread/fwrite.. + can vary...given that it needs to update the hash values in MHT. + So, we need to have another test wrapper..that runs + these cases(1K,2K,4K buf sizes..etc) for various file sizes..*/ +enum apis_measured { FOPEN = 0, FREAD, FWRITE, FFLUSH, FSEEK, FTELL, FCLOSE, MAX_APIS }; + +struct perf_run { + uint8_t* ptr_to_buf; + size_t buf_len; +}; + +void get_time(struct timeval* time); +void compute_duration(struct timeval* t1, struct timeval* t2, float* elapsed); +void get_time_and_compute_duration(struct timeval* t1, struct timeval* t2, float* elapsed); + +void pfs_run_perf_meas_test(const char* path_to_file); + +#endif //_PERF_MEAS_H_ diff --git a/pfs/apps/pfs_app/pfs_app.cpp b/pfs/apps/pfs_app/pfs_app.cpp new file mode 100644 index 0000000..1614584 --- /dev/null +++ b/pfs/apps/pfs_app/pfs_app.cpp @@ -0,0 +1,104 @@ +/* + * License: BSD 3-Clause "New" or "Revised" License + * + * Other web pages for this license + * http://www.opensource.org/licenses/BSD-3-Clause + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pfs_debug.h" + +#include "perf_meas.h" +#include "pfs_app.h" +#include "pfs_dirops.h" + +int main() { + printf("%s, pass\n", __func__); + +#ifdef BASIC_SANITY_TEST_FOR_SUPPORTED_APIS_AND_READDIR_TEST + test_for_apis_supported_for_protected_files(PATH_TO_NON_PFS_TESTFILE, NORMAL); + test_for_apis_supported_for_protected_files(PATH_TO_PFS_TESTFILE1, PROTECTED); + /* Test files get removed, in *supported* api above. + * re-creating , test files to test recursive listing of + files(with file-names decrypted for protected files). */ + create_test_file(PATH_TO_NON_PFS_TESTFILE); + create_test_file(PATH_TO_PFS_TESTFILE1); + create_test_file(PATH_TO_PFS_TESTFILE2); + pfs_readdir_test(); +#endif + +#ifdef PFS_UNSUPPORTED_APIS_TEST + test_for_apis_unsupported_for_protected_files(PATH_TO_NON_PFS_TESTFILE, NORMAL); + test_for_apis_unsupported_for_protected_files(PATH_TO_PFS_TESTFILE1, PROTECTED); +#endif + +#ifdef PFS_BLOCKING_APIS_THAT_RETURN_FILE_DESCRIPTOR + test_for_blocking_apis_that_return_file_descriptor(PATH_TO_NON_PFS_TESTFILE, NORMAL); + test_for_blocking_apis_that_return_file_descriptor(PATH_TO_PFS_TESTFILE1, PROTECTED); +#endif + +#ifdef PFS_MISC_TEST_CASES + test_for_misc_test_cases1(PATH_TO_NON_PFS_TESTFILE, NORMAL); + test_for_misc_test_cases1(PATH_TO_PFS_TESTFILE1, PROTECTED); + test_for_misc_test_cases2(); +#endif + +#ifdef PFS_DIR_APIS_TESTING + pfs_directory_system_apis_test(); +#endif + +#ifdef PFS_64BIT_API_TEST_CASES + test_for_64bit_apis(PATH_TO_NON_PFS_TESTFILE, NORMAL); + test_for_64bit_apis(PATH_TO_PFS_TESTFILE1, PROTECTED); +#endif + +#ifdef PFS_WIDECHAR_UNSUPPORTED_APIS_TEST + test_for_wide_char_apis_unsupported(PATH_TO_NON_PFS_TESTFILE3, NORMAL); + test_for_wide_char_apis_unsupported(PATH_TO_PFS_TESTFILE3, PROTECTED); +#endif + +#ifdef PFS_CONVERT_NORMAL_FILES_TO_PROTECTED_FILES + protect_files(CLEAR_DIR_PATH, PROTECTED_DIR_PATH); +#endif + +#ifdef PFS_API_PERF_MEAS + pfs_run_perf_meas_test(PATH_TO_PFS_TESTFILE1); +#endif + + return 0; +} diff --git a/pfs/apps/pfs_app/pfs_app.h b/pfs/apps/pfs_app/pfs_app.h new file mode 100644 index 0000000..39c4793 --- /dev/null +++ b/pfs/apps/pfs_app/pfs_app.h @@ -0,0 +1,104 @@ +/* + * License: BSD 3-Clause License + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _PFS_APP_H_ +#define _PFS_APP_H_ + +#include "pfs_plus.h" + +/* Note: When application sets path to directory-path(and-also + * when setting path in manifest), + * there should NOT be any trailing backslash(/) */ +#define PROTECTED_DIR_PATH "/pfs_dir/secrets" +#define CLEAR_DIR_PATH "/pfs_dir/non_secrets" +// Note: Below path mounted in manifest +#define TEST_DIR_PATH "/pfs_dir/test_dir" + +#define FILENAME_MAX_LENGTH (PFS_FILENAME_MAX_LENGTH + 30) + +#define FILE_PATH_MAX (4096) +#define MAX_SIZE_FOR_FILE_READ (512) + +#define PATH_TO_PFS_TESTFILE1 "/pfs_dir/secrets/sub_dir1/pfs_file1" +#define PATH_TO_PFS_TESTFILE2 "/pfs_dir/secrets/sub_dir1/pfs_file2" +#define PATH_TO_PFS_TESTFILE3 "/pfs_dir/secrets/sub_dir1/pfs_file3" + +#define PATH_TO_NON_PFS_TESTFILE "/pfs_dir/non_secrets/non_pfs_file1" +#define PATH_TO_NON_PFS_TESTFILE2 "/pfs_dir/non_secrets/non_pfs_file2" +#define PATH_TO_NON_PFS_TESTFILE3 "/pfs_dir/non_secrets/non_pfs_file3" + +#define BUFFER_SIZE (128) +#define SPRINTF_BUFSIZE (300) + +/* Note: Enable testing for different categories of system-apis, +using macros below. */ +#define BASIC_SANITY_TEST_FOR_SUPPORTED_APIS_AND_READDIR_TEST +//#define PFS_UNSUPPORTED_APIS_TEST +//#define PFS_BLOCKING_APIS_THAT_RETURN_FILE_DESCRIPTOR +//#define PFS_MISC_TEST_CASES +//#define PFS_64BIT_API_TEST_CASES +//#define PFS_WIDECHAR_UNSUPPORTED_APIS_TEST +//#define PFS_DIR_APIS_TESTING + +//#define PFS_CONVERT_NORMAL_FILES_TO_PROTECTED_FILES +//#define PFS_API_PERF_MEAS + +#define ZERO_RESULT(ret_val) ((ret_val == 0) ? "PASS" : "ERROR") +#define EXPECTED_TRUE(result) ((result == 1) ? "PASS" : "ERROR") +#define FILE_TYPE_STRING(file_type) ((file_type == PROTECTED) ? "PROTECTED" : "NORMAL") + +typedef enum file_type { NORMAL, PROTECTED, NOT_APPLICABLE } file_type_t; + +int open_file_wrapper(const char* path_to_file, FILE** fp); +int create_test_file(const char* path_to_file); + +int test_for_apis_supported_for_protected_files(const char* path_to_file, file_type_t file_type); +int test_for_apis_unsupported_for_protected_files(const char* path_to_file, file_type_t file_type); + +int test_for_blocking_apis_that_return_file_descriptor(const char* path_to_file, + file_type_t file_type); +int test_for_misc_test_cases1(const char* path_to_file, file_type_t file_type); +int test_for_misc_test_cases2(); + +int test_for_wide_char_apis_unsupported(const char* path_to_file, file_type_t file_type); +int test_for_64bit_apis(const char* path_to_file, file_type_t file_type); + +int protect_files(char* src_dir_path, char* dest_dir_path); + +int check_ret_and_print_dbg(const char* func, file_type_t file_type, int ret_val); +int check_ptr_and_print_dbg(const char* func, file_type_t file_type, void* ptr); +int check_fd_and_print_dbg(const char* func, file_type_t file_type, int fd); +int check_errno_and_print_dbg(const char* func, file_type_t file_type, wint_t ret_val); +int check_bool_and_print_dbg(const char* func, file_type_t file_type, bool result, char* dbg_str); + +#endif /* _PFS_APP_H_ */ diff --git a/pfs/apps/pfs_app/pfs_app.manifest.template b/pfs/apps/pfs_app/pfs_app.manifest.template new file mode 100644 index 0000000..277b84b --- /dev/null +++ b/pfs/apps/pfs_app/pfs_app.manifest.template @@ -0,0 +1,79 @@ +#!$(PAL) + +loader.preload = file:../../deps/graphene/Runtime/libsysdb.so +loader.exec = file:pfs_app +loader.execname = pfs_app +loader.env.PATH = /:/usr/sbin:/usr/bin:/sbin:/bin +loader.env.LD_LIBRARY_PATH = /lib:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu +loader.env.USERNAME = +loader.env.HOME = +loader.env.PWD = +loader.env.PFS_MOUNT_POINT = /pfs_dir/secrets +loader.env.PFS_USE_CUSTOM_KEY = no +loader.env.LD_PRELOAD = libfileops_interceptor.so +#loader.env.LD_DEBUG = all +loader.env.HOSTNAME = test +loader.debug_type = inline +loader.syscall_symbol = syscalldb + +fs.mount.lib1.type = chroot +fs.mount.lib1.path = /lib +fs.mount.lib1.uri = file:../../deps/graphene/Runtime/ + +fs.mount.lib2.type = chroot +fs.mount.lib2.path = /lib/x86_64-linux-gnu +fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu + +fs.mount.usr.type = chroot +fs.mount.usr.path = /usr +fs.mount.usr.uri = file:/usr + +#Below mount point needed for libstdc++, also mount rules for sub-directories +#under a given directory, lets say /usr, should be below the top-level /usr mount rule. +#Just like /lib mount rules above. +fs.mount.lib3.type = chroot +fs.mount.lib3.path = /usr/lib/x86_64-linux-gnu +fs.mount.lib3.uri = file:/usr/lib/x86_64-linux-gnu + +fs.mount.bin.type = chroot +fs.mount.bin.path = /bin +fs.mount.bin.uri = file:/bin + +fs.mount.etc.type = chroot +fs.mount.etc.path = /etc +fs.mount.etc.uri = file: + +fs.mount.pfs.type = chroot +fs.mount.pfs.path = /pfs_dir +fs.mount.pfs.uri = file:/home/skris14/pfs_mount + +# allow to bind on port 8000 +net.allow_bind.1 = 127.0.0.1:8000 +# allow to connect to port 8000 +net.allow_peer.1 = 127.0.0.1:8000 + +# sgx-related +sys.stack.size = 1m +sys.brk.size = 64M +glibc.heap_size = 16M +#sgx.enclave_size = $(MEMSIZE) +sgx.enclave_size = 1G +sgx.thread_num = 4 +#sgx.thread_num = $(THREADNUM) + +sgx.trusted_files.ld = file:../../deps/graphene/Runtime//ld-linux-x86-64.so.2 +sgx.trusted_files.libc = file:../../deps/graphene/Runtime//libc.so.6 +sgx.trusted_files.libdl = file:../../deps/graphene/Runtime//libdl.so.2 +sgx.trusted_files.libm = file:../../deps/graphene/Runtime//libm.so.6 +sgx.trusted_files.libpthread = file:../../deps/graphene/Runtime//libpthread.so.0 +sgx.trusted_files.libcrypt = file:/lib/x86_64-linux-gnu/libcrypt.so.1 +sgx.trusted_files.libstdcplusplus = file:/usr/lib/x86_64-linux-gnu/libstdc++.so.6 +sgx.trusted_files.libgcc = file:/lib/x86_64-linux-gnu/libgcc_s.so.1 +sgx.trusted_files.libfops = file:../../deps/graphene/Runtime//libfileops_interceptor.so +sgx.trusted_files.sealed_custom_key = file:sealed_custom_key + +#Note: Access permissions applies to sub-directories recursively. +#WARNING: There should NOT be any / at the end of the directory-path below, it breaks the +#allowed_files functionality graphenes, so even for files that do NOT exist(for example +#volume-meta-data, graphene returns true(as if file exists). +sgx.allowed_files.mount_path = file:/home/skris14/pfs_mount diff --git a/pfs/apps/pfs_app/pfs_create_protected_files.cpp b/pfs/apps/pfs_app/pfs_create_protected_files.cpp new file mode 100644 index 0000000..73178b4 --- /dev/null +++ b/pfs/apps/pfs_app/pfs_create_protected_files.cpp @@ -0,0 +1,351 @@ +/* + * License: BSD 3-Clause "New" or "Revised" License + * + * Other web pages for this license + * http://www.opensource.org/licenses/BSD-3-Clause + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sgx_tseal.h" + +#include "dirops_typedefs.h" +#include "fileops_typedefs.h" +#include "pfs_debug.h" + +#include "perf_meas.h" +#include "pfs_app.h" +#include "pfs_dirops.h" + +static int pfs_create_new_file(const char* prot_path, const char* filename, FILE** sfp); +static int protect_clear_file(const char* clear_path, const char* clear_file, + const char* prot_path); + +/* if file already exists, returns error */ +static int pfs_create_new_file(const char* prot_path, const char* filename, FILE** sfp) { + uint32_t full_path_len = 0; + char* full_path = NULL; + int ret = 0; + + if (!filename || !sfp || !prot_path) + return -1; + + printf("filename is =%s\n", filename); + + full_path_len = strlen(prot_path) + 1 + strlen(filename) + 1; + + if (full_path_len + 1 > FILE_PATH_MAX) { + printf("file_path=%s too long, length=%d\n", full_path, full_path_len); + return -1; + } + + full_path = (char*)calloc(full_path_len + 1, 1); + + if (!full_path) + return -1; + + strncpy(full_path, prot_path, strlen(prot_path)); + full_path[strlen(prot_path)] = '/'; + strncpy(full_path + strlen(prot_path) + 1, filename, strlen(filename)); + + printf("full_path=%s\n", full_path); + + *sfp = fopen(full_path, "rb"); + + if (*sfp == NULL) { + printf("File does NOT exist, need to CREATE one\n"); + + *sfp = fopen(full_path, "w+b"); + + if (*sfp == NULL) { + printf("File does NOT exist, error in creating file\n"); + ret = -1; + goto exit; + } + } else if (*sfp != NULL) { + printf("File ALREADY exists.\n"); + ret = -1; + goto exit; + } + +exit: + + if (full_path) + free(full_path); + + return ret; +} + +/* opens existing clear file, and outputs protected file */ +static int protect_clear_file(const char* clear_path, const char* clear_file, + const char* prot_path) { + char* buffer = NULL; + int file_size = 0; + + ssize_t bytes_read = 0; + ssize_t read_length = 0; + ssize_t bytes_written = 0; + + int bytes_written_total = 0; + int bytes_read_total = 0; + + FILE* fclear = NULL; + int ret_val = 0; + char* full_path = NULL; + uint32_t full_path_len = 0; + + FILE* sfp = NULL; + + if (!clear_file || !clear_path || !prot_path) + return -1; + + full_path_len = strlen(clear_path) + 1 + strlen(clear_file) + 1; + + if (full_path_len + 1 > FILE_PATH_MAX) { + printf("file_path=%s too long, file_size=%d\n", full_path, full_path_len); + return -1; + } + + full_path = (char*)calloc(full_path_len + 1, 1); + + if (!full_path) + return -1; + + strncpy(full_path, clear_path, strlen(clear_path)); + full_path[strlen(clear_path)] = '/'; + strncpy(full_path + strlen(clear_path) + 1, clear_file, strlen(clear_file)); + + printf("full_path=%s\n", full_path); + + fclear = fopen(full_path, "rb"); + + if (!fclear) { + printf("file doesnt exist, errno=%d, full_path=%s\n", errno, full_path); + ret_val = -1; + goto exit; + } + + ret_val = fseek(fclear, 0, SEEK_END); + + if (ret_val == -1) { + goto exit; + } + + file_size = ftell(fclear); + + if (file_size == -1 || file_size == 0) { + ret_val = -1; + goto exit; + } + + ret_val = fseek(fclear, 0, SEEK_SET); + + if (ret_val == -1) { + goto exit; + } + + printf("Length of file=%s, is =%d\n", full_path, file_size); + + if (MAX_SIZE_FOR_FILE_READ < file_size) { + read_length = MAX_SIZE_FOR_FILE_READ; + } else + read_length = file_size; + + buffer = (char*)malloc(read_length); + + if (!buffer) { + ret_val = -1; + goto exit; + } + + bytes_read_total = 0; + + // opening the protected file. + ret_val = pfs_create_new_file(prot_path, clear_file, &sfp); + + if (ret_val != 0) { + ret_val = -1; + goto exit; + } + + while (bytes_read_total < file_size) { + bytes_read = fread(buffer, sizeof(uint8_t), read_length, fclear); + + printf("Length of buffer is =%lu, bytes read=%lu\n", read_length, bytes_read); + + if (bytes_read != read_length) { + ret_val = ferror(fclear); + + if (ret_val != 0) { + printf("error in fread=%d\n", ret_val); + break; + } + } + + // Write to protected file + bytes_written = fwrite(buffer, sizeof(uint8_t), bytes_read, sfp); + printf("Size of Write= %lu\n", bytes_written); + + if (bytes_written != bytes_read) { + printf("error, ret=%d, size of Write=%lu, bytes_read=%lu\n", ret_val, bytes_written, + bytes_read); + break; + } + + // to protected_file. + bytes_written_total = bytes_written_total + bytes_read; + + bytes_read_total = bytes_read_total + bytes_read; + + if (file_size > bytes_read_total) { + if ((file_size - bytes_read_total) > MAX_SIZE_FOR_FILE_READ) + read_length = MAX_SIZE_FOR_FILE_READ; + else + read_length = file_size - bytes_read_total; + } + } + + if (ret_val == 0) { + printf("file_size =%d, bytes_read_total=%d, bytes_written_total=%d\n", file_size, + bytes_read_total, bytes_written_total); + } + +exit: + if (full_path) + free(full_path); + + if (buffer) + free(buffer); + + if (sfp) + fclose(sfp); + + if (fclear) + fclose(fclear); + + return ret_val; +} + +/* Recursively dives into the source directory, + * and creates protected files into the destination directory. + * It creates new sub-directories in destination, to duplicate + * the directory-tree from source to destination, and transforms. + * clear files into protected files. + */ +int protect_files(char* src_dir_path, char* dest_dir_path) { + int ret = 0; + + DIR* src_dir_ptr; + struct dirent* entry; + char* src_file_path = NULL; + char* dest_file_path = NULL; + + if (!src_dir_path || !dest_dir_path) + return -1; + + if (!(src_dir_ptr = opendir(src_dir_path))) + return -1; + + printf("%s: recursive listing, parent src_dir_ptr=%s\n", __func__, src_dir_path); + + while ((entry = readdir(src_dir_ptr)) != NULL) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + src_file_path = (char*)calloc(strlen(src_dir_path) + 1 + strlen(entry->d_name) + 1, 1); + + if (!src_file_path) { + printf("calloc fails\n"); + goto exit; + } + + strncpy(src_file_path, src_dir_path, strlen(src_dir_path)); + src_file_path[strlen(src_dir_path)] = '/'; + strncpy(src_file_path + strlen(src_dir_path) + 1, entry->d_name, strlen(entry->d_name)); + + dest_file_path = + (char*)calloc(strlen(dest_dir_path) + 1 + strlen(entry->d_name) + 1, 1); + + if (!dest_file_path) { + printf("calloc fails\n"); + goto exit; + } + + strncpy(dest_file_path, dest_dir_path, strlen(dest_dir_path)); + dest_file_path[strlen(dest_dir_path)] = '/'; + strncpy(dest_file_path + strlen(dest_dir_path) + 1, entry->d_name, + strlen(entry->d_name)); + + if ((ret = mkdir(dest_file_path, 0775) != 0) && (errno != EEXIST)) { + printf("mkdir fails for->%s, errno=%d\n", dest_file_path, errno); + goto exit; + } + + printf("[DIRECTORY->%s], type->%d, src_path->%s, dest_path->%s\n", entry->d_name, + entry->d_type, src_file_path, dest_file_path); + protect_files(src_file_path, dest_file_path); + } else if (entry->d_type == DT_REG) { + printf("FILE-> %s, type->%d\n", entry->d_name, entry->d_type); + protect_clear_file(src_dir_path, entry->d_name, dest_dir_path); + } else { + printf("No-op for FILE-> %s, type->%d\n", entry->d_name, entry->d_type); + } + } + +exit: + + if (src_file_path) { + free(src_file_path); + src_file_path = NULL; + } + + if (dest_file_path) { + free(dest_file_path); + dest_file_path = NULL; + } + + closedir(src_dir_ptr); + + return ret; +} diff --git a/pfs/apps/pfs_app/pfs_dirops.cpp b/pfs/apps/pfs_app/pfs_dirops.cpp new file mode 100644 index 0000000..df3fd6b --- /dev/null +++ b/pfs/apps/pfs_app/pfs_dirops.cpp @@ -0,0 +1,759 @@ +/* + * License: BSD 3-Clause License + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "fileops_typedefs.h" +#include "perf_meas.h" +#include "pfs_app.h" +#include "pfs_debug.h" + +static void using_readdir_api(const char* name); +static void using_readdir64_api(const char* name); + +static void using_readdir_r_api(const char* name); +static void using_readdir64_r_api(const char* name); + +static int using_scandir_api(const char* dir_path); +static int using_scandir64_api(const char* dir_path); +static int using_scandirat_api(const char* dir_path); +static int using_scandirat64_api(const char* dir_path); + +static void using_readdir_api(const char* name) { + DIR* dir; + struct dirent* entry; + char* file_path = NULL; + + if (!(dir = opendir(name))) + return; + + printf("%s: recursive listing, parent dir=%s\n", __func__, name); + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(name) + 1 + strlen(entry->d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + + strncpy(file_path, name, strlen(name)); + file_path[strlen(name)] = '/'; + strncpy(file_path + strlen(name) + 1, entry->d_name, strlen(entry->d_name)); + + printf("[DIRECTORY->%s], type->%d\n", entry->d_name, entry->d_type); + using_readdir_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", entry->d_name, entry->d_type); + } + } + + if (file_path) { + free(file_path); + file_path = NULL; + } + + closedir(dir); +} + +static void using_readdir64_api(const char* name) { + DIR* dir; + struct dirent64* entry; + char* file_path = NULL; + + if (!(dir = opendir(name))) + return; + + printf("%s: recursive listing, parent dir=%s\n", __func__, name); + + while ((entry = readdir64(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(name) + 1 + strlen(entry->d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + + strncpy(file_path, name, strlen(name)); + file_path[strlen(name)] = '/'; + strncpy(file_path + strlen(name) + 1, entry->d_name, strlen(entry->d_name)); + + printf("[DIRECTORY->%s], type->%d\n", entry->d_name, entry->d_type); + using_readdir64_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", entry->d_name, entry->d_type); + } + } + + if (file_path) { + free(file_path); + file_path = NULL; + } + + closedir(dir); +} + +static void using_readdir_r_api(const char* name) { + DIR* dir; + struct dirent entry; + struct dirent* result = NULL; + char* file_path = NULL; + int ret = 0; + + if (!(dir = opendir(name))) + return; + + printf("%s: recursive listing, parent dir=%s\n", __func__, name); + + memset(&entry, 0, sizeof(entry)); + + while ((ret = readdir_r(dir, &entry, &result)) == 0 && result != NULL) { + if (entry.d_type == DT_DIR) { + if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(name) + 1 + strlen(entry.d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + + strncpy(file_path, name, strlen(name)); + file_path[strlen(name)] = '/'; + strncpy(file_path + strlen(name) + 1, entry.d_name, strlen(entry.d_name)); + + printf("[DIRECTORY->%s], type->%d\n", entry.d_name, entry.d_type); + using_readdir_r_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", entry.d_name, entry.d_type); + } + } + + if (file_path) { + free(file_path); + file_path = NULL; + } + + closedir(dir); +} + +static void using_readdir64_r_api(const char* name) { + DIR* dir; + struct dirent64 entry; + struct dirent64* result = NULL; + char* file_path = NULL; + int ret = 0; + + if (!(dir = opendir(name))) + return; + + printf("%s: recursive listing, parent dir=%s\n", __func__, name); + + memset(&entry, 0, sizeof(entry)); + + while ((ret = readdir64_r(dir, &entry, &result)) == 0 && result != NULL) { + if (entry.d_type == DT_DIR) { + if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(name) + 1 + strlen(entry.d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + + strncpy(file_path, name, strlen(name)); + file_path[strlen(name)] = '/'; + strncpy(file_path + strlen(name) + 1, entry.d_name, strlen(entry.d_name)); + + printf("[DIRECTORY->%s], type->%d\n", entry.d_name, entry.d_type); + using_readdir64_r_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", entry.d_name, entry.d_type); + } + } + + if (file_path) { + free(file_path); + file_path = NULL; + } + + closedir(dir); +} + +static int using_scandir_api(const char* dir_path) { + struct dirent** namelist; + int n; + char* file_path = NULL; + + if (!dir_path) + return -1; + + n = scandir(dir_path, &namelist, NULL, alphasort); + + printf("%s:%d: dir_path=%s, n=%d\n", __func__, __LINE__, dir_path, n); + + if (n < 0) { + perror("scandir"); + return n; + } + + while (n--) { + printf("%s\n", namelist[n]->d_name); + + if (namelist[n]->d_type == DT_DIR) { + if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(dir_path) + 1 + strlen(namelist[n]->d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + + strncpy(file_path, dir_path, strlen(dir_path)); + file_path[strlen(dir_path)] = '/'; + strncpy(file_path + strlen(dir_path) + 1, namelist[n]->d_name, + strlen(namelist[n]->d_name)); + + printf("[DIRECTORY->%s], type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + using_scandir_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + } + + free(namelist[n]); + } + + free(namelist); + + if (file_path) { + free(file_path); + file_path = NULL; + } + + return 0; +} + +static int using_scandir64_api(const char* dir_path) { + struct dirent64** namelist; + int n; + char* file_path = NULL; + + if (!dir_path) + return -1; + + n = scandir64(dir_path, &namelist, NULL, NULL); + + printf("%s:%d: dir_path=%s, n=%d\n", __func__, __LINE__, dir_path, n); + + if (n < 0) { + perror("scandir64"); + return n; + } + + while (n--) { + printf("%s\n", namelist[n]->d_name); + + if (namelist[n]->d_type == DT_DIR) { + if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(dir_path) + 1 + strlen(namelist[n]->d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + strncpy(file_path, dir_path, strlen(dir_path)); + file_path[strlen(dir_path)] = '/'; + strncpy(file_path + strlen(dir_path) + 1, namelist[n]->d_name, + strlen(namelist[n]->d_name)); + + printf("[DIRECTORY->%s], type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + using_scandir64_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + } + + free(namelist[n]); + } + + free(namelist); + + if (file_path) { + free(file_path); + file_path = NULL; + } + + return 0; +} + +static int using_scandirat_api(const char* dir_path) { + struct dirent** namelist; + int n; + char* file_path = NULL; + + if (!dir_path) + return -1; + + // note: passing absolute path, so fd can be NULL(i.e. it is ignored). + n = scandirat(NULL, dir_path, &namelist, NULL, NULL); + + printf("%s:%d: dir_path=%s, n=%d\n", __func__, __LINE__, dir_path, n); + + if (n < 0) { + perror("scandirat"); + return n; + } + + while (n--) { + printf("%s\n", namelist[n]->d_name); + + if (namelist[n]->d_type == DT_DIR) { + if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(dir_path) + 1 + strlen(namelist[n]->d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + + strncpy(file_path, dir_path, strlen(dir_path)); + file_path[strlen(dir_path)] = '/'; + strncpy(file_path + strlen(dir_path) + 1, namelist[n]->d_name, + strlen(namelist[n]->d_name)); + + printf("[DIRECTORY->%s], type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + using_scandirat_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + } + + free(namelist[n]); + } + + free(namelist); + + if (file_path) { + free(file_path); + file_path = NULL; + } + + return 0; +} + +static int using_scandirat64_api(const char* dir_path) { + struct dirent64** namelist; + int n; + char* file_path = NULL; + + if (!dir_path) + return -1; + + // note: passing absolute path, so fd can be NULL(i.e. it is ignored). + n = scandirat64(NULL, dir_path, &namelist, NULL, NULL); + + printf("%s:%d: dir_path=%s, n=%d\n", __func__, __LINE__, dir_path, n); + + if (n < 0) { + perror("scandirat64"); + return n; + } + + while (n--) { + printf("%s\n", namelist[n]->d_name); + + if (namelist[n]->d_type == DT_DIR) { + if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) + continue; + + file_path = (char*)calloc(strlen(dir_path) + 1 + strlen(namelist[n]->d_name) + 1, 1); + + if (!file_path) { + printf("calloc fails\n"); + continue; + } + strncpy(file_path, dir_path, strlen(dir_path)); + file_path[strlen(dir_path)] = '/'; + strncpy(file_path + strlen(dir_path) + 1, namelist[n]->d_name, + strlen(namelist[n]->d_name)); + + printf("[DIRECTORY->%s], type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + using_scandirat64_api(file_path); + } else { + printf("FILE-> %s, type->%d\n", namelist[n]->d_name, namelist[n]->d_type); + } + + free(namelist[n]); + } + + free(namelist); + + if (file_path) { + free(file_path); + file_path = NULL; + } + + return 0; +} + +static void using_other_dirops_apis(const char* name) { + DIR* dir; + struct dirent* entry; + char* file_path = NULL; + int fd = 0; + long int dir_offset = 0; + + if (!(dir = opendir(name))) + return; + + printf("%s: recursive listing, parent dir=%s\n", __func__, name); + + fd = dirfd(dir); + + printf("fd=%d after call to dirfd, parent dir path=%s\n", fd, name); + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + printf("[DIRECTORY->%s], type->%d\n", entry->d_name, entry->d_type); + } else { + printf("FILE->%s, type->%d\n", entry->d_name, entry->d_type); + + // testing other apis..and then exiting + dir_offset = telldir(dir); + + if (dir_offset == -1) { + printf("error = %d, from telldir\n", dir_offset); + break; + } else { + seekdir(dir, dir_offset); + rewinddir(dir); + printf("exiting loop, after other apis are called\n"); + break; + } + } + } + + if (file_path) { + free(file_path); + file_path = NULL; + } + + closedir(dir); +} + +#ifndef USE_FDS +#define USE_FDS 16 +#endif + +int list_entry(const char* filepath, const struct stat* info, const int typeflag, + struct FTW* pathinfo) { + if (!filepath || !info || !pathinfo || typeflag < 0) { + return -1; + } + + return 0; +} + +int using_nftw_api(const char* const dirpath) { + int result = 0; + + if (dirpath == NULL || *dirpath == '\0') { + return EINVAL; + } + + result = nftw(dirpath, list_entry, USE_FDS, FTW_PHYS); + if (result >= 0) + errno = result; + + return errno; +} + +int list_entry64(const char* filepath, const struct stat64* info, const int typeflag, + struct FTW* pathinfo) { + if (!filepath || !info || !pathinfo || typeflag < 0) { + return -1; + } + + return 0; +} + +int using_nftw64_api(const char* const dirpath) { + int result = 0; + + if (dirpath == NULL || *dirpath == '\0') { + return EINVAL; + } + + result = nftw64(dirpath, list_entry64, USE_FDS, FTW_PHYS); + if (result >= 0) + errno = result; + + return errno; +} + +int pfs_readdir_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_readdir_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_readdir64_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_readdir64_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_readdir_r_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_readdir_r_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_readdir64_r_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_readdir64_r_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_scandir_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_scandir_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_scandir64_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_scandir64_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_scandirat_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_scandirat_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_scandirat64_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_scandirat64_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_other_dirops_apis_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + CLEAR_DIR_PATH, PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_other_dirops_apis(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_nftw_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + /*CLEAR_DIR_PATH, */ PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_nftw_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_nftw64_test() { + uint32_t i; + int ret_val = 0; + + const char* dir_names_abs[] = { + /*CLEAR_DIR_PATH, */ PROTECTED_DIR_PATH, + }; + + printf("%s,%d:\n", __func__, __LINE__); + + for (i = 0; i < sizeof(dir_names_abs) / sizeof(char*); i++) { + using_nftw64_api(dir_names_abs[i]); + } + + return ret_val; +} + +int pfs_directory_system_apis_test() { + int overall_ret = 0; + + overall_ret += pfs_readdir_test(); + overall_ret += pfs_readdir64_test(); + overall_ret += pfs_readdir_r_test(); + overall_ret += pfs_readdir64_r_test(); + overall_ret += pfs_scandir_test(); + overall_ret += pfs_scandir64_test(); + overall_ret += pfs_scandirat_test(); + overall_ret += pfs_scandirat64_test(); + overall_ret += pfs_other_dirops_apis_test(); + overall_ret += pfs_nftw_test(); + overall_ret += pfs_nftw64_test(); + + printf("\n#######%s, %s#######\n", __func__, ZERO_RESULT(overall_ret)); + + return overall_ret; +} diff --git a/pfs/apps/pfs_app/pfs_dirops.h b/pfs/apps/pfs_app/pfs_dirops.h new file mode 100644 index 0000000..e2beafa --- /dev/null +++ b/pfs/apps/pfs_app/pfs_dirops.h @@ -0,0 +1,51 @@ +/* + * License: BSD 3-Clause License + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _PFS_DIROPS_H_ +#define _PFS_DIROPS_H_ + +int pfs_directory_system_apis_test(); + +int pfs_readdir_test(); +int pfs_readdir64_test(); +int pfs_readdir_r_test(); +int pfs_readdir64_r_test(); +int pfs_scandir_test(); +int pfs_scandir64_test(); +int pfs_scandirat_test(); +int pfs_scandirat64_test(); +int pfs_nftw_test(); +int pfs_nftw64_test(); +int pfs_other_dirops_apis_test(); + +#endif /* _PFS_DIROPS_H_ */ diff --git a/pfs/apps/pfs_app/pfs_fileops_apis.cpp b/pfs/apps/pfs_app/pfs_fileops_apis.cpp new file mode 100644 index 0000000..848bc8f --- /dev/null +++ b/pfs/apps/pfs_app/pfs_fileops_apis.cpp @@ -0,0 +1,1215 @@ +/* + * License: BSD 3-Clause "New" or "Revised" License + * + * Other web pages for this license + * http://www.opensource.org/licenses/BSD-3-Clause + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sgx_tseal.h" + +#include "dirops_typedefs.h" +#include "fileops_typedefs.h" +#include "pfs_debug.h" + +#include "perf_meas.h" +#include "pfs_app.h" +#include "pfs_dirops.h" + +char sprintf_buf_glb[SPRINTF_BUFSIZE]; + +static int pfs_character_string_apis_test(FILE* sfp, file_type_t file_type); +static int pfs_getc_ungetc_test(FILE* sfp, file_type_t file_type); + +static int pfs_file_offset_apis_test(const char* path_to_file, file_type_t file_type); +static int pfs_secure_io_path_test(); +static int pfs_app_long_filename_test(); +static int system_apis_open_close(const char* filename, file_type); +static int pfs_rename_test(const char* path_to_file, file_type_t file_type); + +static int invoke_vfscanf(FILE* stream, const char* format, ...); +static int invoke_vfprintf(FILE* stream, const char* format, ...); +static int test_vfscanf_vfprintf(FILE* stream, file_type_t file_type); + +static int pfs_getc_ungetc_test(FILE* sfp, file_type_t file_type) { + size_t file_len = 0; + uint8_t string_to_write[] = "Trial run"; + int32_t ret_val = 0; + + char byte_read; + char byte_from_ungetc; + long offset; + long offset_after_ungetc; + + unsigned int i; + int overall_ret = 0; + + if (!sfp) { + printf("\nDEBUG: Null pointer to %s", __func__); + return -1; + } + + ret_val = fseek(sfp, 0L, SEEK_SET); + + file_len = fwrite(string_to_write, sizeof(uint8_t), sizeof(string_to_write), sfp); + + printf("DEBUG, after fwrite, file_len = %d\n", (int)file_len); + + ret_val = fflush(sfp); + + fseek(sfp, 0, SEEK_END); + file_len = (unsigned long)ftell(sfp); + + ret_val = fseek(sfp, 0L, SEEK_SET); + + printf("DEBUG, file_len = %lu\n", file_len); + + for (i = 1; i <= file_len; i++) { + byte_read = getc(sfp); + offset = ftell(sfp); + + if (byte_read == 'r') { + byte_from_ungetc = ungetc(byte_read, sfp); + offset_after_ungetc = ftell(sfp); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, + "byte_read=%c, byte_from_ungetc=%c. offset=%ld, offset_after_ungetc=%ld", + byte_read, byte_from_ungetc, offset, offset_after_ungetc); + overall_ret += check_bool_and_print_dbg( + "getc_ungetc", file_type, + ((byte_read == byte_from_ungetc) && ((offset_after_ungetc + 1) == offset)), + sprintf_buf_glb); + + break; + } + } + + return overall_ret; +} + +// Note: Test works fine... +static int pfs_character_string_apis_test(FILE* sfp, file_type_t file_type) { + int32_t ret_val = 0; + int overall_ret = 0; + uint8_t read_buffer[BUFFER_SIZE]; + + const char string_to_write[] = "Trial run"; + const char test_string[] = "You said what"; + + int byte_read = 0; + int byte_written = 0; + + uint64_t offset = 0; + + if (!sfp) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + memset(read_buffer, 0, sizeof(read_buffer)); + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + overall_ret += check_bool_and_print_dbg("fseek", file_type, (ret_val == 0), (char*)""); + + // Read from File + offset = ftell(sfp); + // printf("DEBUG: File offset = %lu\n", offset); + overall_ret += check_bool_and_print_dbg("ftell", file_type, (offset == 0), (char*)""); + + byte_read = fgetc(sfp); + offset = ftell(sfp); + + byte_written = putc(byte_read, sfp); + offset = ftell(sfp); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "getc = %d, putc = %d", byte_read, byte_written); + overall_ret += check_bool_and_print_dbg("fgetc_putc", file_type, (byte_written == byte_read), + sprintf_buf_glb); + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + offset = ftell(sfp); + // printf("DEBUG: File offset = %lu\n", offset); + + byte_read = fgetc(sfp); + offset = ftell(sfp); + + byte_written = fputc(byte_read, sfp); + offset = ftell(sfp); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "getc = %d, putc = %d", byte_read, byte_written); + overall_ret += + check_bool_and_print_dbg("fgetc_fputc", file_type, (byte_written == byte_read), (char*)""); + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + if (sizeof(read_buffer) >= sizeof(string_to_write)) { + fgets((char*)read_buffer, sizeof(string_to_write), sfp); + } + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + ret_val = fputs((char*)test_string, sfp); + // printf("ret_val from fputs: %d\n", ret_val); + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + if (sizeof(read_buffer) >= sizeof(test_string)) { + fgets((char*)read_buffer, sizeof(test_string), sfp); + } + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "fgets = %s, fputs = %s", read_buffer, test_string); + overall_ret += check_bool_and_print_dbg( + "fputs_fgets", file_type, + (strncmp((const char*)read_buffer, test_string, strlen(test_string)) == 0), (char*)""); + + overall_ret += pfs_getc_ungetc_test(sfp, file_type); + + return overall_ret; +} + +static int pfs_file_offset_apis_test(const char* path_to_file, file_type_t file_type) { + FILE* sfp = NULL; + uint8_t read_buffer[BUFFER_SIZE]; + int32_t ret_val = 0; + int overall_ret = 0; + + if (!path_to_file) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + ret_val = open_file_wrapper(path_to_file, &sfp); + + if (ret_val != 0) { + return ret_val; + } + + memset(read_buffer, 0, sizeof(read_buffer)); + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + // Read from File + int64_t file_offset = 0; + file_offset = ftell(sfp); + // printf("DEBUG: File offset = %lu\n", file_offset); + + fpos_t position; + + ret_val = fgetpos(sfp, &position); + + overall_ret += check_ret_and_print_dbg("fgetpos", file_type, ret_val); + + fputs("Hello, World!", sfp); + + ret_val = fsetpos(sfp, &position); + + overall_ret += check_ret_and_print_dbg("fsetpos", file_type, ret_val); + + fputs("This is going to override previous content", sfp); + + ret_val = fseeko(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseeko, ret = %d\n", (int)ret_val); + overall_ret += check_bool_and_print_dbg("fseeko", file_type, (ret_val == 0), (char*)""); + + // Read from File + file_offset = ftello(sfp); + // printf("DEBUG: File offset = %ld\n", file_offset); + overall_ret += check_bool_and_print_dbg("ftello", file_type, (file_offset == 0), (char*)""); + + ret_val = fclose(sfp); + + // printf("DEBUG, after fclose, ret_val = %d\n", (int)ret_val); + + return overall_ret; +} + +static int pfs_secure_io_path_test() { + FILE* fp = NULL; + unsigned int i; + int ret_val = 0; + int overall_ret = 0; + + const char* file_names[] = {"/pfs_dir/non_secrets/temp100", "/pfs_dir/secrets/temp100"}; + + const char* file_names_rel[] = {"../non_secrets/temp101", "../secrets/temp101"}; + + const char* file_names_invalid[] = {"../non_sec/temp101", "/pfs_dir/sec/temp100"}; + + for (i = 0; i < sizeof(file_names) / sizeof(char*); i++) { + fp = fopen(file_names[i], "w+"); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "filepath=%s", file_names[i]); + + overall_ret += + check_bool_and_print_dbg("fopen", NOT_APPLICABLE, (fp != NULL), (char*)sprintf_buf_glb); + + if (fp) { + fclose(fp); + fp = NULL; + } + } + + ret_val = chdir(TEST_DIR_PATH); + + if (ret_val != 0) { + printf("chdir to %s, returned error, ret_val=%d, errno=%d\n", TEST_DIR_PATH, ret_val, + errno); + overall_ret += -1; + goto exit; + } + + for (i = 0; i < sizeof(file_names_rel) / sizeof(char*); i++) { + fp = fopen(file_names_rel[i], "w+"); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "filepath=%s", file_names_rel[i]); + + overall_ret += + check_bool_and_print_dbg("fopen", NOT_APPLICABLE, (fp != NULL), (char*)sprintf_buf_glb); + + if (fp) { + fclose(fp); + fp = NULL; + } + } + + for (i = 0; i < sizeof(file_names_invalid) / sizeof(char*); i++) { + fp = fopen(file_names_invalid[i], "r"); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "filepath=%s", file_names_invalid[i]); + + overall_ret += + check_bool_and_print_dbg("fopen", NOT_APPLICABLE, (fp == NULL), (char*)sprintf_buf_glb); + + if (fp) { + fclose(fp); + fp = NULL; + } + } + +exit: + + printf("%s, %d: ret_val->%d\n", __func__, __LINE__, overall_ret); + + return overall_ret; +} + +// Note: Test API for low-level system apis that use file-descriptor +// like open/close..to ensure it works fine for normal files(i.e. non-protected files). +static int system_apis_open_close(const char* filename, file_type_t file_type) { + int ret = 0; + int overall_ret = 0; + int fd = -1; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + ssize_t len = 0; + uint8_t string_to_write[] = "^^Low-level-APIs->Trial run^^"; + + if (filename == NULL || strnlen(filename, 1) == 0) { + printf("filename is NULL or empty\n"); + return -1; + } + + // create the file if it doesn't exists, read-only/read-write + fd = open(filename, O_CREAT | O_RDWR | O_APPEND, mode); + + overall_ret += check_fd_and_print_dbg("open", file_type, fd); + + len = write(fd, string_to_write, sizeof(string_to_write)); + + overall_ret += check_bool_and_print_dbg( + "write", file_type, ((file_type == NORMAL && (len == sizeof(string_to_write))) || + (file_type == PROTECTED && len == -1)), + (char*)""); + + ret = close(fd); + overall_ret += check_ret_and_print_dbg("close", file_type, ret); + + return overall_ret; +} + +static int pfs_app_long_filename_test() { + int ret = 0; + int overall_ret = 0; + char filename[FILENAME_MAX_LENGTH]; + char* file_path = NULL; + uint32_t cnt = 0; + FILE* fp = NULL; + + memset(filename, 0, sizeof(filename)); + + for (cnt = 0; cnt < sizeof(filename) - 1; cnt++) { + filename[cnt] = 'a'; + } + + file_path = (char*)calloc(strlen(CLEAR_DIR_PATH) + 1 + sizeof(filename) + 1, 1); + + if (file_path != NULL) { + strncpy(file_path, CLEAR_DIR_PATH, strlen(CLEAR_DIR_PATH)); + file_path[strlen(CLEAR_DIR_PATH)] = '/'; + strncpy(file_path + strlen(CLEAR_DIR_PATH) + 1, filename, strlen(filename)); + + // printf("filename=%s, strlen(filename)=%lu\n", filename, strlen(filename)); + // printf("file-path=%s, strlen(file-path)=%lu\n", file_path, strlen(file_path)); + + fp = fopen((const char*)file_path, "wb+"); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "filepath=%s", file_path); + + overall_ret += + check_bool_and_print_dbg("fopen", NOT_APPLICABLE, (fp != NULL), (char*)sprintf_buf_glb); + + if (fp) { + ret = fclose(fp); + ret = remove(file_path); + } + } + + if (file_path) { + free(file_path); + } + + return overall_ret; +} + +static int pfs_rename_test(const char* path_to_file, file_type_t file_type) { + int overall_ret = 0; + int rename_ret = 0; + const char rename_suffix[] = "_rename"; + char* renamed_path = NULL; + + renamed_path = + (char*)calloc((strlen(path_to_file) + strlen(rename_suffix) + 1), sizeof(uint8_t)); + + if (!renamed_path) { + printf("malloc failed"); + return -1; + } + + strncpy(renamed_path, path_to_file, strlen(path_to_file)); + + renamed_path = + strncat(renamed_path, (const char*)rename_suffix, strlen((const char*)rename_suffix)); + + printf("path to rename to=%s\n", renamed_path); + + rename_ret = rename(path_to_file, renamed_path); + + overall_ret += check_ret_and_print_dbg("rename", file_type, rename_ret); + + /*Note: issue https://github.com/oscarlab/graphene/pull/911/ + * rename has been resolved in latest version of graphene.*/ + if (rename_ret == 0) { + rename_ret = rename(renamed_path, path_to_file); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, + "undoing of rename, old path=%s, new path=%s, ret_val=%d", path_to_file, + renamed_path, rename_ret); + overall_ret += check_bool_and_print_dbg("rename", file_type, + ((file_type == NORMAL && rename_ret == 0) || + (file_type == PROTECTED && rename_ret == -1)), + sprintf_buf_glb); + } + + rename_ret = renameat(AT_FDCWD, path_to_file, AT_FDCWD, renamed_path); + + overall_ret += check_bool_and_print_dbg( + "renameat", file_type, + ((file_type == NORMAL && rename_ret == -1) || (file_type == PROTECTED && rename_ret == -1)), + ""); + + if (rename_ret == 0) { + rename_ret = renameat(AT_FDCWD, renamed_path, AT_FDCWD, path_to_file); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, + "undoing of renameat, old path=%s, new path=%s, ret_val=%d", path_to_file, + renamed_path, rename_ret); + overall_ret += check_bool_and_print_dbg("rename", file_type, + ((file_type == NORMAL && rename_ret == 0) || + (file_type == PROTECTED && rename_ret == -1)), + sprintf_buf_glb); + } + + if (renamed_path) + free(renamed_path); + + return overall_ret; +} + +static int invoke_vfscanf(FILE* stream, const char* format, ...) { + int ret = 0; + va_list args; + va_start(args, format); + ret = vfscanf(stream, format, args); + + va_end(args); + + return ret; +} + +static int invoke_vfprintf(FILE* stream, const char* format, ...) { + int ret = 0; + + va_list args; + va_start(args, format); + ret = vfprintf(stream, format, args); + + va_end(args); + + return ret; +} + +static int test_vfscanf_vfprintf(FILE* sfp, file_type_t file_type) { + int ret_val = 0; + bool result = 0; + char test_string[100]; + int overall_ret = 0; + + if (!sfp) + return -1; + + errno = 0; + + ret_val = invoke_vfscanf(sfp, "%ls", test_string); + + result = 0; + if ((file_type == NORMAL /*&& errno == 0*/) || (file_type == PROTECTED && ret_val == -1)) { + result = 1; + } + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "vfscanf's ret = %d, errno=%d", ret_val, errno); + + overall_ret += check_bool_and_print_dbg("vfscanf", file_type, result, (char*)sprintf_buf_glb); + + ret_val = invoke_vfprintf(sfp, "String with variable %ls.\n", "arguments"); + + result = 0; + if ((file_type == NORMAL /*&& errno == 0*/) || (file_type == PROTECTED && ret_val == -1)) { + result = 1; + } + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "vfprintf's ret = %d, errno=%d", ret_val, errno); + + overall_ret += check_bool_and_print_dbg("vfprintf", file_type, result, (char*)""); + + return overall_ret; +} + +int check_ret_and_print_dbg(const char* func, file_type_t file_type, int ret_val) { + int ret = 0; + bool result = 0; + + if (!func) + return -1; + + if (file_type == PROTECTED) { + result = (ret_val == -1) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, ret_val=%d, errno=%d\n", func, EXPECTED_TRUE(result), + "PROTECTED", ret_val, errno); + } else { + result = (ret_val == 0) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, ret_val=%d, errno=%d\n", func, EXPECTED_TRUE(result), + "NORMAL", ret_val, errno); + } + + // Note: Helps to verify that over-riding apis set the correct errno + errno = 0; + + return (ret | !result); +} + +int check_ptr_and_print_dbg(const char* func, file_type_t file_type, void* ptr) { + int ret = 0; + bool result = 0; + + if (!func) + return -1; + + if (file_type == PROTECTED) { + result = (ptr == NULL) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, ptr=%p, errno=%d\n", func, EXPECTED_TRUE(result), + "PROTECTED", ptr, errno); + } else { + result = (ptr != NULL) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, ptr=%p, errno=%d\n", func, EXPECTED_TRUE(result), "NORMAL", + ptr, errno); + } + + // Note: Helps to verify that over-riding apis set the correct errno + errno = 0; + + return (ret | !result); +} + +int check_fd_and_print_dbg(const char* func, file_type_t file_type, int fd) { + int ret = 0; + bool result = 0; + + if (!func) + return -1; + + if (file_type == PROTECTED) { + result = (fd == -1) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, fd=%d, errno=%d\n", func, EXPECTED_TRUE(result), + "PROTECTED", fd, errno); + } else { + result = (fd > 0) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, fd=%d, errno=%d\n", func, EXPECTED_TRUE(result), "NORMAL", + fd, errno); + } + + // Note: Helps to verify that over-riding apis set the correct errno + errno = 0; + + return (ret | !result); +} + +int check_errno_and_print_dbg(const char* func, file_type_t file_type, wint_t ret_val) { + int ret = 0; + bool result = 0; + + if (!func) + return -1; + + if (file_type == PROTECTED) { + result = (errno == ENOTSUP) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, errno=%d, ret_val=%d\n", func, EXPECTED_TRUE(result), + "PROTECTED", errno, ret_val); + } else { + result = (errno == 0) ? 1 : 0; + + printf("\n%s, %s, file_type=%s, errno=%d, ret_val=%d\n", func, EXPECTED_TRUE(result), + "NORMAL", errno, ret_val); + } + + // Note: Helps to verify that over-riding apis set the correct errno + errno = 0; + + return (ret | !result); +} + +int check_bool_and_print_dbg(const char* func, file_type_t file_type, bool result, char* dbg_str) { + int ret = 0; + + if (!func) + return -1; + + printf("\n%s, %s, file_type=%s, errno=%d, dbg_str->%s\n", func, EXPECTED_TRUE(result), + (file_type == PROTECTED) ? "PROTECTED" + : ((file_type == NORMAL) ? "NORMAL" : "NOT_APPLICABLE"), + errno, dbg_str); + + // Note: Helps to verify that over-riding apis set the correct errno + errno = 0; + + return (ret | !result); +} + +int open_file_wrapper(const char* path_to_file, FILE** fp) { + int ret = 0; + FILE* sfp = NULL; + + if (!path_to_file || !fp) + return -1; + + printf("\nsizeof(path)=%lu, file-name=%s\n", strlen(path_to_file), path_to_file); + + sfp = fopen((const char*)path_to_file, "rb"); + + if (sfp == NULL) { + printf("DEBUG, creating new testfile in wb+ mode \n"); + + sfp = fopen((const char*)path_to_file, "wb+"); + } else { + fclose(sfp); + // Note: changed mode from ab+ to wb+ to shorten + // debug output of file content, to ease debugging. + /*printf("DEBUG, opening existing file in ab+ mode\n"); + sfp = fopen((const char*)path_to_file, "ab+");*/ + printf("DEBUG, opening existing file in wb+ mode\n"); + sfp = fopen((const char*)path_to_file, "wb+"); + } + + if (!sfp) { + printf("DEBUG, %s:%d, error sfp=%p is NULL\n", __func__, __LINE__, sfp); + return -1; + } else { + *fp = sfp; + } + + return ret; +} + +int create_test_file(const char* path_to_file) { + FILE* sfp = NULL; + size_t buffer_len = 0; + uint8_t string_to_write[] = "Trial run"; + int32_t ret_val = 0; + + if (!path_to_file) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + ret_val = open_file_wrapper(path_to_file, &sfp); + + if (ret_val != 0) { + return ret_val; + } + + buffer_len = fwrite(string_to_write, sizeof(uint8_t), sizeof(string_to_write), sfp); + // printf("DEBUG, after fwrite, buffer_len = %d\n", (int)buffer_len); + + ret_val = fclose(sfp); + // printf("DEBUG, after fclose, ret_val = %d\n", (int)ret_val); + + return ret_val; +} + +// Note: Test works fine... +int test_for_apis_supported_for_protected_files(const char* path_to_file, file_type_t file_type) { + FILE* sfp = NULL; + size_t buffer_len = 0; + uint8_t string_to_write[] = "Trial run"; + uint8_t read_buffer[BUFFER_SIZE]; + int32_t ret_val = 0; + int overall_ret = 0; + + bool result = 0; + errno = 0; + + if (!path_to_file) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + ret_val = open_file_wrapper(path_to_file, &sfp); + + if (ret_val != 0) { + return ret_val; + } + + buffer_len = fwrite(string_to_write, sizeof(uint8_t), sizeof(string_to_write), sfp); + + overall_ret += check_bool_and_print_dbg("fwrite", file_type, + (buffer_len == sizeof(string_to_write)), (char*)""); + + ret_val = fflush(sfp); + + overall_ret += check_bool_and_print_dbg("fflush", file_type, (ret_val == 0), (char*)""); + + ret_val = ferror(sfp); + + printf("DEBUG, after ferror, ret_val = %d\n", (int)ret_val); + + memset(read_buffer, 0, sizeof(read_buffer)); + + /*Note: Application needs to do a seek to reset + the file offset to the start of the file, i.e if it tries + to do a file read, after doing a write */ + ret_val = fseek(sfp, 0L, SEEK_SET); + + overall_ret += check_bool_and_print_dbg("fseek", file_type, (ret_val == 0), (char*)""); + + buffer_len = fread(read_buffer, sizeof(uint8_t), sizeof(read_buffer), sfp); + // printf("DEBUG, after fread, file_len = %d\n", (int)buffer_len); + + overall_ret += check_bool_and_print_dbg("fread", file_type, (buffer_len > 0), (char*)""); + + // Read from File + uint64_t file_offset = 0; + file_offset = ftell(sfp); + // printf("DEBUG: File offset = %lu\n", file_offset); + + overall_ret += check_bool_and_print_dbg("ftell", file_type, (file_offset != -1), (char*)""); + + printf("DEBUG: Stuff fread from file byte output\n"); + + for (unsigned int cnt = 0; cnt < buffer_len; cnt++) { + printf("%c", read_buffer[cnt]); + if (!((cnt + 1) % 12)) + printf("\n"); + } + + ret_val = feof(sfp); + overall_ret += check_bool_and_print_dbg("feof", file_type, (ret_val != -1), (char*)""); + + clearerr(sfp); + + ret_val = feof(sfp); + + // printf("DEBUG, after feof, ret_val = %d\n", (int)ret_val); + + overall_ret += pfs_character_string_apis_test(sfp, file_type); + + ret_val = fclose(sfp); + overall_ret += check_bool_and_print_dbg("fclose", file_type, (ret_val != -1), (char*)""); + + errno = 0; + ret_val = remove(path_to_file); + overall_ret += check_bool_and_print_dbg("remove", file_type, (ret_val == 0), (char*)""); + + printf("MOUNT_POINT : %s\n", getenv("PFS_MOUNT_POINT")); + + printf("\n#######%s, file_type=%s, %s#######\n", __func__, FILE_TYPE_STRING(file_type), + ZERO_RESULT(overall_ret)); + + return overall_ret; +} + +// Note: Test works fine...except for issues commented below. +int test_for_apis_unsupported_for_protected_files(const char* path_to_file, file_type_t file_type) { + FILE* sfp = NULL; + FILE* fp = NULL; + int fd = 0; + int overall_ret = 0; + int val = 0; + + uint8_t read_buffer[BUFFER_SIZE]; + int32_t ret_val = 0; + + uint8_t test_string[] = "You said what"; + + if (!path_to_file) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + errno = 0; + + ret_val = open_file_wrapper(path_to_file, &sfp); + + if (ret_val != 0) { + return ret_val; + } + + memset(read_buffer, 0, sizeof(read_buffer)); + + ret_val = fseek(sfp, 0L, SEEK_SET); + // printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + // Read from File + uint64_t file_offset = 0; + file_offset = ftell(sfp); + printf("DEBUG: File offset = %lu\n", file_offset); + + fd = fileno(sfp); + + overall_ret += check_fd_and_print_dbg("fileno", file_type, fd); + + fp = freopen(path_to_file, "rw+", sfp); + + overall_ret += check_ptr_and_print_dbg("freopen", file_type, (void*)fp); + + if (fp != NULL) { + /* As per man-page, after successful freopen, the previous + * stream is closed, so should be using the old stream pointer */ + sfp = fp; + } + + fpos_t position; + + ret_val = fgetpos(sfp, &position); + + overall_ret += check_ret_and_print_dbg("fgetpos", file_type, ret_val); + + ret_val = fsetpos(sfp, &position); + + overall_ret += check_ret_and_print_dbg("fsetpos", file_type, ret_val); + + /*// Note: add test code below...keeping it commented..for reference... + if (file_type == NORMAL) { + wchar_t wide_str[100]; + + ret_val = fprintf(stdout, "fprintf-PRINT TO STDOUT\n"); + printf("\nafter fprintf, ret_val=%d, errno=%d\n", ret_val, errno); + + ret_val = fwprintf(stdout, L"fwprintf-PRINT TO STDOUT\n"); + printf("\nafter fwprintf, ret_val=%d, errno=%d\n", ret_val, errno); + + ret_val = fscanf(stdin, "%d", &val); + printf("\nafter fscanf, ret_val=%d, errno=%d\n", ret_val, errno); + + ret_val = fwscanf(stdin, L" %ls", wide_str); + printf("\nafter fwscanf, ret_val=%d, errno=%d\n", ret_val, errno); + }*/ + + ret_val = fprintf(sfp, "%d", val); + + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "fprintf's ret = %d, errno=%d", ret_val, errno); + overall_ret += check_bool_and_print_dbg( + "fprintf", file_type, + ((file_type == NORMAL && errno == 0) || (file_type == PROTECTED && ret_val == -1)), + (char*)sprintf_buf_glb); + + ret_val = fscanf(sfp, "%d", &val); + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "fscanf's ret = %d, errno=%d", ret_val, errno); + overall_ret += check_bool_and_print_dbg( + "fscanf", file_type, + ((file_type == NORMAL && errno == 0) || (file_type == PROTECTED && ret_val == -1)), + (char*)sprintf_buf_glb); + + ret_val = fwprintf(sfp, L"wide-char string\n"); + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "fwprintf's ret = %d, errno=%d", ret_val, errno); + + overall_ret += check_bool_and_print_dbg( + "fwprintf", file_type, + ((file_type == NORMAL && errno == 0) || (file_type == PROTECTED && ret_val == -1)), + (char*)sprintf_buf_glb); + + ret_val = fwscanf(sfp, (wchar_t*)"%d", &val); + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "fwscanf's ret = %d, errno=%d", ret_val, errno); + + overall_ret += check_bool_and_print_dbg( + "fwscanf", file_type, + ((file_type == NORMAL && errno == 0) || (file_type == PROTECTED && ret_val == -1)), + (char*)fwscanf); + + setbuf(sfp, (char*)test_string); + overall_ret += check_errno_and_print_dbg("setbuf", file_type, 0); + + setbuffer(sfp, (char*)test_string, sizeof(test_string)); + overall_ret += check_errno_and_print_dbg("setbuffer", file_type, 0); + + setlinebuf(sfp); + overall_ret += check_errno_and_print_dbg("setlinebuf", file_type, 0); + + ret_val = setvbuf(sfp, (char*)test_string, _IONBF, sizeof(test_string)); + overall_ret += check_ret_and_print_dbg("setvbuf", file_type, ret_val); + + ret_val = fclose(sfp); + + overall_ret += pfs_rename_test(path_to_file, file_type); + + overall_ret += test_vfscanf_vfprintf(sfp, file_type); + + printf("\n#######%s, file_type=%s, %s#######\n", __func__, FILE_TYPE_STRING(file_type), + ZERO_RESULT(overall_ret)); + + return overall_ret; +} + +// Note: Test works fine... +int test_for_blocking_apis_that_return_file_descriptor(const char* path_to_file, + file_type_t file_type) { + int ret = 0; + int overall_ret = 0; + int fd; + + int flags = O_CREAT; + + mode_t mode = S_IRWXU | S_IRGRP | S_IROTH; + + errno = 0; + + fd = creat(path_to_file, mode); + + overall_ret += check_fd_and_print_dbg("creat", file_type, fd); + + ret = close(fd); + + overall_ret += check_ret_and_print_dbg("close", file_type, ret); + + fd = open(path_to_file, flags, mode); + + overall_ret += check_fd_and_print_dbg("open", file_type, fd); + + ret = close(fd); + + overall_ret += check_ret_and_print_dbg("close", file_type, ret); + + flags = O_RDWR; + + fd = open(path_to_file, flags); + + // printf("fd=%d, after open without mode\n", fd); + overall_ret += check_fd_and_print_dbg("open", file_type, fd); + + ret = close(fd); + + overall_ret += check_ret_and_print_dbg("close", file_type, ret); + + fd = open(path_to_file, O_TMPFILE | O_RDWR, mode); + + // printf("fd=%d, after open using O_TMPFILE\n", fd); + overall_ret += check_fd_and_print_dbg("open", file_type, fd); + + close(fd); + + flags = O_CREAT; + + fd = openat(0, path_to_file, flags, mode); + + // printf("fd=%d, after openat\n", fd); + overall_ret += check_fd_and_print_dbg("openat", file_type, fd); + + flags = O_RDWR; + + fd = openat(0, path_to_file, flags); + + // printf("fd=%d, after openat without mode\n", fd); + overall_ret += check_fd_and_print_dbg("openat", file_type, fd); + + close(fd); + + printf("\n#######%s, file_type=%s, %s#######\n", __func__, FILE_TYPE_STRING(file_type), + ZERO_RESULT(overall_ret)); + + return overall_ret; +} + +int test_for_misc_test_cases1(const char* path_to_file, file_type_t file_type) { + int overall_ret = 0; + + errno = 0; + + overall_ret += pfs_file_offset_apis_test(path_to_file, file_type); + overall_ret += system_apis_open_close(path_to_file, file_type); + + printf("\n#######%s, file_type=%s, %s#######\n", __func__, FILE_TYPE_STRING(file_type), + ZERO_RESULT(overall_ret)); + + return overall_ret; +} + +int test_for_misc_test_cases2() { + int overall_ret = 0; + + errno = 0; + + overall_ret += pfs_secure_io_path_test(); + overall_ret += pfs_app_long_filename_test(); + + printf("\n#######%s, %s#######\n", __func__, ZERO_RESULT(overall_ret)); + + return overall_ret; +} + +static int open_file_wrapper_64bit(const char* path_to_file, FILE** fp) { + int ret = 0; + FILE* sfp = NULL; + + if (!path_to_file || !fp) + return -1; + + printf("sizeof(path)=%lu, file-name=%s\n", strlen(path_to_file), path_to_file); + + sfp = fopen64((const char*)path_to_file, "rb+"); + + if (sfp == NULL) { + printf("DEBUG, creating new testfile in wb+ mode \n"); + + sfp = fopen64((const char*)path_to_file, "w+b"); + } else { + fclose(sfp); + printf("DEBUG, opening existing file in ab+ mode\n"); + + sfp = fopen64((const char*)path_to_file, "a+b"); + } + + if (!sfp) { + printf("DEBUG, %s:%d, error sfp=%p is NULL\n", __func__, __LINE__, sfp); + return -1; + } else { + *fp = sfp; + } + + return ret; +} + +static int blocking_64bit_apis_that_return_file_descriptor(const char* path_to_file, + file_type_t file_type) { + int ret_val = 0; + int overall_ret = 0; + + int fd; + + int flags = O_CREAT; + + mode_t mode = S_IRWXU | S_IRGRP | S_IROTH; + + fd = creat64(path_to_file, mode); + + overall_ret += check_fd_and_print_dbg("creat64", file_type, fd); + + close(fd); + + fd = open64(path_to_file, flags, mode); + + overall_ret += check_fd_and_print_dbg("open64", file_type, fd); + + ret_val = close(fd); + + overall_ret += check_ret_and_print_dbg("close", file_type, ret_val); + + flags = O_RDWR; + + // open without mode + fd = open64(path_to_file, flags); + + overall_ret += check_fd_and_print_dbg("open64", file_type, fd); + + ret_val = close(fd); + + overall_ret += check_ret_and_print_dbg("close", file_type, ret_val); + + // open using O_TMPFILE + fd = open64(path_to_file, O_TMPFILE | O_RDWR, mode); + + overall_ret += check_fd_and_print_dbg("open64", file_type, fd); + + close(fd); + + flags = O_CREAT; + + fd = openat64(0, path_to_file, flags, mode); + + overall_ret += check_fd_and_print_dbg("openat64", file_type, fd); + + flags = O_RDWR; + + // openat without mode + fd = openat64(0, path_to_file, flags); + + overall_ret += check_fd_and_print_dbg("openat64", file_type, fd); + + close(fd); + + return overall_ret; +} + +static int pfs_override_test_64bit(const char* path_to_file, file_type_t file_type) { + FILE* sfp = NULL; + size_t buffer_len = 0; + uint8_t string_to_write[] = "Trial run"; + uint8_t read_buffer[BUFFER_SIZE]; + int32_t ret_val = 0; + int overall_ret = 0; + + if (!path_to_file) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + ret_val = open_file_wrapper_64bit(path_to_file, &sfp); + + if (ret_val != 0) { + return ret_val; + } + + buffer_len = fwrite(string_to_write, sizeof(uint8_t), sizeof(string_to_write), sfp); + printf("DEBUG, after fwrite, buffer_len = %d\n", (int)buffer_len); + + ret_val = fflush(sfp); + printf("DEBUG, after fflush, ret_val = %d\n", (int)ret_val); + + memset(read_buffer, 0, sizeof(read_buffer)); + + ret_val = fseeko64(sfp, 0L, SEEK_SET); + printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + buffer_len = fread(read_buffer, sizeof(uint8_t), sizeof(read_buffer), sfp); + printf("DEBUG, after fread, file_len = %d\n", (int)buffer_len); + + // Read from File + uint64_t file_offset = 0; + file_offset = ftello64(sfp); + printf("DEBUG: File offset = %lu\n", file_offset); + + printf("DEBUG: Stuff fread from file byte output\n"); + + for (unsigned int cnt = 0; cnt < buffer_len; cnt++) { + printf("%c", read_buffer[cnt]); + if (!((cnt + 1) % 12)) + printf("\n"); + } + + fpos64_t position; + FILE* new_fp = NULL; + + ret_val = fgetpos64(sfp, &position); + + overall_ret += check_ret_and_print_dbg("fgetpos64", file_type, ret_val); + + fputs("Hello, World!", sfp); + + ret_val = fsetpos64(sfp, &position); + + overall_ret += check_ret_and_print_dbg("fsetpos64", file_type, ret_val); + + // If filename is NOT specified..it will close the original + // file and re-open with the new mode. + new_fp = freopen64("", "rw+", sfp); + + overall_ret += check_ptr_and_print_dbg("freopen64", file_type, (void*)new_fp); + + fclose(sfp); + + return overall_ret; +} + +int test_for_64bit_apis(const char* path_to_file, file_type_t file_type) { + int overall_ret = 0; + + errno = 0; + + overall_ret += pfs_override_test_64bit(path_to_file, file_type); + overall_ret += blocking_64bit_apis_that_return_file_descriptor(path_to_file, file_type); + + printf("\n#######%s, file_type=%s, %s#######\n", __func__, FILE_TYPE_STRING(file_type), + ZERO_RESULT(overall_ret)); + + return overall_ret; +} diff --git a/pfs/apps/pfs_app/pfs_wide_char_apis.cpp b/pfs/apps/pfs_app/pfs_wide_char_apis.cpp new file mode 100644 index 0000000..d4f70f7 --- /dev/null +++ b/pfs/apps/pfs_app/pfs_wide_char_apis.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "pfs_debug.h" + +#include "perf_meas.h" +#include "pfs_app.h" +#include "pfs_dirops.h" + +extern char sprintf_buf_glb[SPRINTF_BUFSIZE]; + +static int wide_format_vfwscanf(FILE* stream, const wchar_t* format, ...) { + int ret = 0; + va_list args; + va_start(args, format); + ret = vfwscanf(stream, format, args); + + va_end(args); + + return ret; +} + +static int wide_format_vfwprintf(FILE* stream, const wchar_t* format, ...) { + int ret = 0; + + va_list args; + va_start(args, format); + ret = vfwprintf(stream, format, args); + + va_end(args); + + return ret; +} + +int test_for_wide_char_apis_unsupported(const char* path_to_file, file_type_t file_type) { + FILE* sfp = NULL; + int ret_val = 0; + + wint_t wc; + wint_t wc2; + wchar_t test_string[100]; + wchar_t* ret_str = NULL; + + bool result = 0; + int overall_ret = 0; + + errno = 0; + + if (!path_to_file) { + printf("\nDEBUG: invalid params to %s", __func__); + return -1; + } + + ret_val = open_file_wrapper(path_to_file, &sfp); + + if (ret_val != 0) { + return ret_val; + } + + fputs("Hello World\n", sfp); + + fclose(sfp); + + sfp = fopen(path_to_file, "ab+"); + if (!sfp) { + perror("Can't open file for reading"); + return EXIT_FAILURE; + } + + ret_val = fseek(sfp, 0L, SEEK_SET); + printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + uint64_t file_offset = 0; + file_offset = ftell(sfp); + printf("DEBUG: File offset = %lu\n", file_offset); + + while ((wc = fgetwc(sfp)) != WEOF) { + putwchar(wc); + } + + overall_ret += check_errno_and_print_dbg("fgetwc", file_type, wc); + + ret_val = fseek(sfp, 0L, SEEK_SET); + printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + while ((wc = getwc(sfp)) != WEOF) { + putwchar(wc); + } + + overall_ret += check_errno_and_print_dbg("getwc", file_type, wc); + + ret_val = fseek(sfp, 0L, SEEK_SET); + printf("DEBUG, after fseek, ret = %d\n", (int)ret_val); + + wc = getwc(sfp); + wc2 = ungetwc(wc, sfp); + + overall_ret += check_errno_and_print_dbg("ungetwc", file_type, wc); + + ret_val = fseek(sfp, 0L, SEEK_SET); + wc = fputwc(L'I', sfp); + overall_ret += check_errno_and_print_dbg("fputwc", file_type, wc); + + wc = putwc(L'J', sfp); + overall_ret += check_errno_and_print_dbg("putwc", file_type, wc); + + ret_val = fseek(sfp, 0L, SEEK_SET); + + ret_str = fgetws(test_string, sizeof(test_string), sfp); + + result = 0; + if (file_type == NORMAL) { + if (ret_str != NULL) { + if (wcsncmp(ret_str, test_string, wcslen(test_string)) == 0) { + result = 1; + } + } + } else if (file_type == PROTECTED && ret_str == NULL && errno == ENOTSUP) { + result = 1; + } + + overall_ret += check_bool_and_print_dbg("fgetws", file_type, result, (char*)""); + + ret_val = fputws(test_string, sfp); + result = 0; + if ((file_type == NORMAL && ret_val > 0) || (file_type == PROTECTED && ret_val == -1)) { + result = 1; + } + overall_ret += check_bool_and_print_dbg("fputws", file_type, result, (char*)""); + + ret_val = fwide(sfp, 0); + + result = 0; + if ((file_type == NORMAL && ret_val > 0) || (file_type == PROTECTED && ret_val == 0)) { + result = 1; + } + overall_ret += check_bool_and_print_dbg("fwide", file_type, result, (char*)""); + + ret_val = wide_format_vfwscanf(sfp, L" %ls", test_string); + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "vfwscanf's ret = %d, errno=%d", ret_val, errno); + + result = 0; + if ((file_type == NORMAL && errno == 0) || (file_type == PROTECTED && ret_val == -1)) { + result = 1; + } + overall_ret += check_bool_and_print_dbg("vfwscanf", file_type, result, (char*)sprintf_buf_glb); + + ret_val = wide_format_vfwprintf(sfp, L"String with wide-char variable %ls.\n", L"arguments"); + snprintf(sprintf_buf_glb, SPRINTF_BUFSIZE, "vfwprintf's ret = %d, errno=%d", ret_val, errno); + + result = 0; + if ((file_type == NORMAL && errno == 0) || (file_type == PROTECTED && ret_val == -1)) { + result = 1; + } + overall_ret += check_bool_and_print_dbg("vfwprintf", file_type, result, (char*)sprintf_buf_glb); + + fclose(sfp); + + printf("\n#######%s, file_type=%s, %s#######\n", __func__, FILE_TYPE_STRING(file_type), + ZERO_RESULT(overall_ret)); + + return overall_ret; +} diff --git a/pfs/apps/pfs_app/run_app.sh b/pfs/apps/pfs_app/run_app.sh new file mode 100755 index 0000000..769ee5a --- /dev/null +++ b/pfs/apps/pfs_app/run_app.sh @@ -0,0 +1 @@ +SGX=1 ./pal_loader pfs_app diff --git a/pfs/apps/pfs_app/sealed_custom_key b/pfs/apps/pfs_app/sealed_custom_key new file mode 100644 index 0000000..d88a9a4 Binary files /dev/null and b/pfs/apps/pfs_app/sealed_custom_key differ diff --git a/pfs/build_deps.sh b/pfs/build_deps.sh new file mode 100755 index 0000000..863b986 --- /dev/null +++ b/pfs/build_deps.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +#set -x + +DEFAULT_GRAPHENE_PATH=./deps/graphene +install_graphene=0 + +check_for_graphene_install() +{ + while true + do + echo "If you dont have graphene installed, this script will install graphene under default directory-path->"$DEFAULT_GRAPHENE_PATH + echo -n "Do you already have graphene installed in a non-default location? [yes/no] : " + read ANSWER + + if [ "$ANSWER" == "yes" ]; then + install_graphene=0 + break + elif [ "$ANSWER" == "no" ]; then + install_graphene=1 + break + else + echo "you replied="$ANSWER", please type yes or no" + fi + done + + echo "install_graphene var set to= "$install_graphene + +} + +if [[ ! -d $DEFAULT_GRAPHENE_PATH ]] ; then + check_for_graphene_install +else + echo "graphene already setup in default path->"$DEFAULT_GRAPHENE_PATH +fi + +# You need the SGX SDK and PSW installed. + +mkdir -p deps +pushd deps + +if [ ! -d mbedtls ] ; then + git clone https://github.com/ARMmbed/mbedtls.git + pushd mbedtls + git checkout mbedtls-2.16.2 + patch -p1 < ../../mbedtls_config_file_aesni.patch || exit 1 + popd +fi + +# Linux SGX SDK code +# WARNING!! version of linux_sgx(i.e sgx_2.1.3) should +# match with the one on top of which sgx-protectedfs is patched-for +# in build_pfs_sdk.sh. Since both libraries(libfileops_interceptor.so +# and libpfs_sdk.a should refer to the same version of sgx header files during +# build and for compatibility. +# Except for some header files under sdk/protected_fs which have been +# modified with this patch. +if [[ ! -d linux-sgx ]] ; then + git clone https://github.com/01org/linux-sgx.git + pushd linux-sgx + git checkout sgx_2.1.3 + popd +fi + +#After installing dependencies, we can +#build sources in pfs_sdk directory, outside of deps directory. +popd +if [[ -d pfs_sdk ]] ; then + pushd pfs_sdk + ./build_pfs_sdk.sh || exit 1 + popd +fi +pushd deps + +if [[ ! -d linux-sgx-driver ]] ; then + git clone https://github.com/01org/linux-sgx-driver.git + pushd linux-sgx-driver + git checkout sgx_driver_2.1 + popd +fi + + +if [[ ! -d $DEFAULT_GRAPHENE_PATH && $install_graphene -eq 1 ]] ; then + git clone --recursive https://github.com/oscarlab/graphene.git + pushd graphene + #Note: below graphene commit works fine. + #git checkout aa9743dbcedeffe26ed71debeb07fe7ca4231bd7 + openssl genrsa -3 -out Pal/src/host/Linux-SGX/signer/enclave-key.pem 3072 + # The Graphene build process requires two inputs: (i) SGX driver directory, (ii) driver version. + # cannot use make -j`nproc` with Graphene's build process. + cd Pal/src/host/Linux-SGX/sgx-driver + printf "$(readlink -f ../../../../../../linux-sgx-driver)\n2.1\n" | ./link-intel-driver.py + cd - + printf "$(readlink -f ../linux-sgx-driver)\n2.1\n" | make SGX=1 || exit 1 + + # reduces the effort in the Graphene-SGX manifest file. + ln -s /usr/lib/x86_64-linux-gnu/libprotobuf-c.so.1 Runtime/ + ln -s /usr/lib/libsgx_uae_service.so Runtime/ + ln -s /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 Runtime/ + ln -s /lib/x86_64-linux-gnu/libz.so.1 Runtime/ + ln -s /lib/x86_64-linux-gnu/libssl.so.1.0.0 Runtime/ + + popd +fi + +popd # deps diff --git a/pfs/build_library.sh b/pfs/build_library.sh new file mode 100755 index 0000000..0abbe89 --- /dev/null +++ b/pfs/build_library.sh @@ -0,0 +1,5 @@ +make clean +cd protected_fs_sdk +./build_protected_fs_sdk.sh +cd - +make all diff --git a/pfs/docs/design.md b/pfs/docs/design.md new file mode 100755 index 0000000..db153d6 --- /dev/null +++ b/pfs/docs/design.md @@ -0,0 +1,616 @@ +Design of the Graphene Protected Filesystem Shield +=============================================================================== +* **Author:** *Sudha Krishnakumar *, *Michael Steiner, * +* **Version:** *0.4* + +# Context and Problem Statement # + +The *Graphene Filesystem Shield* is extends *[Graphene](https://grapheneproject.io/)*, a system +which allows existing binaries to run inside SGX, with +transparent protection of file I/O. More specifically, it +transparently intercepts all _file I/O_ calls and, if the implied +filename is below some pre-specified mount-points, cryptographically +protects the corresponding operations while interacting with the +untrusted filesystem. The (hierachy of) files below a +specific mount-points is called a _volume_. Each volume is represented +also as a sub-directory (hierachy) in the untrusted filesystem (in the +current version there is a one-to-one correspondence of files to the +trusted space in addition to a few meta-data files although that might +change in the future and no assumptions should be made on structure +and content). + +## Security Properties ## + +Let us first list security properties which we might desire from such +a system. Later we will discuss their relative importance and which +can be handled where and how. + +* File Content + * *File Content Confidentiality (**FCC**)*: All file content is + hidden from an attacker. + * *File Content Integrity (**FCI**)*: No attacker is able to tamper + with file content without detection. + +* File Metadata + * *Filename Integrity (**FNI**)*: No attacker is able to tamper + with basename of a file without detection, e.g., she cannot rename + protected files. + * *Filename Confidentiality (**FNC**)*: The basename of files is + hidden from attackers. + * *File Attribute Integrity (**FAI**)*: File attributes like + permissions, file-type (e.g., symbolic links), owners or + modification times cannot be tampered with. + * *File Attribute Confidentiality (**FAC**)*: File attributes like + permissions, owners or modification times are hidden from attacker. + * *File Existence Integrity (**FEI**)*: An attacker cannot hide a + previously created protected file. + +* Volume Metadata + * *Path Integrity (**PI**)*: An adversary cannot tamper with the + complete pathname inside a volume, i.e., he cannot move a protected + file to a different sub-directory under the same mount-point. + * *Directory Name Confidentiality (**DNC**)*: An attacker doesn't + learn the names of directories. + * *Volume Binding (**VB**)*: Protected files are tied to a + specific volume, i.e., an attacker cannot move or duplicate files + from one volume to another one volume, whether they are mounted + for the same graphene application instance at two different + mount-points or two different graphene application instances at + the same mount-point path. + +* Other + * *Rollback Protection (**RP**)*: An adversary is unable to revert the + state to earlier version. State can refer to (parts of a) files as + well as meta-data (e.g., existence of file). + * *Access Patterns Hiding (**APH**)*: Prevent the adversary from inferring + information on file content based on access patterns. Some aspects + of are *File Existence Hiding*, *Path Hiding* or alike. + + +## ProtectFS ## +The SGX SDK already provides file-based encryption and integrity +protection in form of the ProtectFS functions (see [SGX Developer Guide](https://software.intel.com/sites/default/files/managed/47/19/sgx-sdk-developer-reference-for-windows-os.pdf). More specifically, it provides FCC, FCI and +FNI. As it assumes a flat non-hierachical namespace, PI, DNC, VB are +out-of-scope. + +It is targeted for greenfield applications, which usually can ensure +that filenames do not leak sensitive information. Hence the absence of +FNC is not an issue. Similarly, such applications can easily adapt to +the absence of FAI, FAC and FEI. + +RP is currently not supported. However, the architecture and code is +prepared for rapid enablement once a scalable monotonic counter +solution exists. + +APH is also considered out-of-scope: Susceptability of attacks +exploiting access patterns is highly application dependent and +requires more sophisticated exploitation techniques. If protection is +required, the application might have to adopt "usual" side-channel +protection techniques for file access and/or a protected file-system +based (expensive) ORAM techiques might have to be implemented. + + +## Implications for Graphene Filesystem Shield ## + +Basing the filesystem shield on ProtectFS seems a natural approach. As +mentioned above, it already provides key security properties. However, +in the Graphene case, we have no control over assumptions made by +application, e.g., filenames could contain sensitive information, +graphene exposes a hierchical filenname space and the application +might require multiple volumes mounted to multiple mount points. Hence +FNC, DNC, PI and VB cannot be as easily dismissed. + +In the following, we discuss some lightweight extensions to ProtectFS +which can provide some of these properties. + +RP, as mentioned in the section on ProtectFS, will have to wait until +there is a scalable monotonic counter solution integrated into +ProtectFS, but then will naturally translate also to the shield. + +To provide strong RP covering also meta-data. To achieve this, one +would require a quite different approached based on block-level +security, like dm-crypt, dm-verity and/or +[dm-x](https://github.com/anrinch/dmx) with an in-graphene filesystem +(similar to [lkl-sgx](https://github.com/lsds/sgx-lkl)). This would +also provide some form of) ABH. However, it would require a +considerable larger effort to build and is currently out-of-scope. + + +# Design Overview # + +File I/O is intercepted (based on an `LD_PRELOAD` handler) and files +behind a mount-point are protected using ProtectFS. However, +additionally, we preprocess filenames such that the names visible to +the untrusted system is encrypted and additionally tied to volume and +path-name. + +More specifically + +- each volume has associate *volume meta data* containing volumne ID, + key-type and information required for key-derivation such as + MRSIGNER, CPUSVN, ISVSVN and KEY-ID. The volume meta data is created + and managed by shield and stored in (hidden/special) file in root + directory of a volume. The metadata should be bound to the graphene + manifest for pre-existing volumes so a party interacting with the + graphene application can track use of volumes across application + instances based on attestation. + +- For each intercepted graphene application functions involving + filenames in protected volumes, we encrypts file-name's path + components before passing the call with encrypted filename to + ProtectFS or other related functions (e.g., opendir). The + encryption performed based on a deterministic tweakable wide-block + cipher with the filename padded to equal length and with (hash of) + volumne-id and path-prefix as tweak. + +- Similarly, intercepted functions where a filename is passed + from the untrusted system, e.g., readdir, will be decrypted and + passed in clear to the graphene application. + +The encryption will ensure FNC and, potentially, DNC. Redundancy in +the filename encoding ensures that anything violating PI and VB is +detected during decryption. + +The design supports both key-management types of ProtectFS, `seal` and +`custom`. See below for more details on the key-management. In +particular, note though that custom keys passed to the shield are +_not_ passed to protectfs but used to derive a separate custom key +which is passed to ProtectFS. + + +# Design Details # + +## Volume Metadata ## + +### Data structures ### + + struct { + protected-data: struct { + key-type: { "custom", "seal" } + key-derivation data: struct { + key-id: sgx_key_id_t + union { + case key-type=="custom": + case key-type=="seal": struct { + cpusvn: SVN + isvsvn: SVN, + MRSIGNER + } + } + } + } + vol-id = MAC_VMK(protected-data) + } + +`MAC` is `CMAC-AES128` [[NIST SP800-38B]](https://csrc.nist.gov/publications/detail/sp/800-38b/archive/2005-05-01). +The key `VMK` is defined below in section keys. + +- **MAC Encoding in Voume-meta-data:** Encoding of payload for MAC (i.e., vol-id of + protected-data blob) is the byte-sequence of a c-struct + corresponding to `protected-data` from above.
+ +### Volume Metadata File name ### +- A (hidden) file named as `.protectfs_vol_md.json` under (volume root +directory, referred as `PFS_MOUNT_POINT` in graphene manifest).
+**TODO (For implementation)**: Current implementation outputs a binary file +`.protectfs_vol_md.bin`, will be changed, once json format is supported. + +### Volume Metadata File encoding ### +- Will be encoded in json, so that it is human readable, and allows + for easy extraction of the volume-id (which is required + to set `vol-id` field in manifest!)
+**TODO (For implementation)**: Currently implementation stores this file +in binary format. To be changed to json in future. + + +### Volume binding in Graphene Manifest ### +The Graphene manifest will be extended with following +(per-volume/mountpoint) values: +- *`PFS_MOUNT_POINT` (referred as `mount-point` in this doc)* : + the mount point in the graphene internal namespace + below the volume is mounted (already existing in current implementation) +- *`PFS_VOL_ID` (referred as `vol-id` in this doc)*: the id of a pre-existing +volume to mount (optional, + mutually exclusive with `allow-reuse`) +- *`PFS_ALLOW_REUSE` (referred as `allow-reuse` in this doc)* : defines whether + in absence of `vol-id` an existing volume under the mount point can be re-used + or a new volume must be created (optional, mutually exclusive with `vol-id`, + default false) + +Below we will refer to them as mf.mount-point, mf.vol-id and +mf.allow-reuse for some particular manifest mf + +**TODO (For implementation)**: + In the future, if implementation supports multiple volumes, naming scheme is + likely to change. For example, each volume's attributes can be grouped + under a label, and set in the manifest (somewhat matching the mount point + mappings in the standard graphene manifest, like `PFS.mount.