Compare commits

...

5 Commits

Author SHA1 Message Date
4f98a4834e
Updated gitignore 2025-05-04 17:10:58 +02:00
8a530b493c
Added new tests for the new dialogue 2025-05-04 17:10:19 +02:00
c4ae40c7bd
Finished the new dialogue functionality 2025-05-04 17:09:28 +02:00
ecedbbb4ce
Added DENY_TEMP access type 2025-05-04 17:05:44 +02:00
10d2988761
Added a version check for the icfs-dialogue 2025-05-04 17:05:07 +02:00
12 changed files with 226 additions and 40 deletions

3
.gitignore vendored
View File

@ -6,6 +6,9 @@ test/.pt.db
*compile_commands.json *compile_commands.json
test/perf* test/perf*
test/callgraph* test/callgraph*
test/openers
test/opener/opener
test/opener/opener.o
src/gui/ui/* src/gui/ui/*
src/gui/*.o src/gui/*.o
src/gui/icfs_dialogue src/gui/icfs_dialogue

View File

@ -1,6 +1,6 @@
#ifndef ACCESS_T_H #ifndef ACCESS_T_H
#define ACCESS_T_H #define ACCESS_T_H
typedef enum { DENY, ALLOW, ALLOW_TEMP, NDEF } access_t; typedef enum { DENY, ALLOW, ALLOW_TEMP, DENY_TEMP, NDEF } access_t;
#endif // !ACCESS_T_H #endif // !ACCESS_T_H

View File

@ -138,8 +138,13 @@ static int on_command_line(GApplication *app, GApplicationCommandLine *cmdline,
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc == 2 && strcmp(argv[1], "--version") == 0) {
fprintf(stdout, "icfs_dialogue 1.0.0");
}
// Create a new application // Create a new application
AdwApplication *app = adw_application_new("com.example.zenityclone", AdwApplication *app = adw_application_new("de.umbrasolis.icfs_dialogue",
G_APPLICATION_HANDLES_COMMAND_LINE); G_APPLICATION_HANDLES_COMMAND_LINE);
g_signal_connect(app, "command-line", G_CALLBACK(on_command_line), NULL); g_signal_connect(app, "command-line", G_CALLBACK(on_command_line), NULL);

View File

@ -9,6 +9,7 @@
#include "perm_permissions_table.h" #include "perm_permissions_table.h"
#include "access_t.h" #include "access_t.h"
#include "process_info.h" #include "process_info.h"
#include "set_mode_t.h"
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <sqlite3.h> #include <sqlite3.h>
@ -83,10 +84,21 @@ static int check_table_col_schema(void *notused, int argc, char **argv,
} }
static int set_flag(void *flag, int argc, char **argv, char **colname) { static int set_flag(void *flag, int argc, char **argv, char **colname) {
(void)argc;
(void)argv;
(void)colname; (void)colname;
*(int *)flag = 1;
if (argc < 3) {
fprintf(stderr,
"Unexpected amount of arguments given to the callback: %d.\n",
argc);
return 1;
}
if (atoi(argv[2])) {
fprintf(stderr, "Third column was: %s\n", argv[2]);
*(int *)flag = 1;
} else {
*(int *)flag = -1;
}
return 0; return 0;
} }
@ -182,7 +194,7 @@ int init_perm_permissions_table(const char *db_filename) {
/** /**
* Destroys the permanent permissions table. * Destroys the permanent permissions table.
*/ */
void destroy_perm_permissions_table() { sqlite3_close(perm_database); } void destroy_perm_permissions_table(void) { sqlite3_close(perm_database); }
/** /**
* Checks if the process has a permanent access to the file. * Checks if the process has a permanent access to the file.
@ -197,7 +209,7 @@ access_t check_perm_access(const char *filename, struct process_info pi) {
char *query = NULL; char *query = NULL;
int ret = asprintf(&query, int ret = asprintf(&query,
"SELECT * FROM %s WHERE executable = \'%s\' " "SELECT * FROM %s WHERE executable = \'%s\' "
"AND filename = \'%s\' AND mode = TRUE;", "AND filename = \'%s\';",
table_name, pi.name, filename); table_name, pi.name, filename);
if (ret < 0) { if (ret < 0) {
@ -220,9 +232,12 @@ access_t check_perm_access(const char *filename, struct process_info pi) {
return NDEF; return NDEF;
} }
if (flag) { if (flag == 1) {
return ALLOW; return ALLOW;
} }
if (flag == -1) {
return DENY;
}
return NDEF; return NDEF;
} }
@ -233,10 +248,19 @@ access_t check_perm_access(const char *filename, struct process_info pi) {
* @param pi: The process information * @param pi: The process information
* @return: 0 on success, 1 on failure * @return: 0 on success, 1 on failure
*/ */
int give_perm_access(const char *filename, struct process_info pi) { int set_perm_access(const char *filename, struct process_info pi,
set_mode_t mode) {
char *query = NULL; char *query = NULL;
int ret = asprintf(&query, "INSERT INTO %s VALUES (\'%s\', \'%s\', TRUE);", int ret = -1;
table_name, pi.name, filename); if (mode == SET_ALLOW) {
ret = asprintf(&query, "INSERT INTO %s VALUES (\'%s\', \'%s\', TRUE);",
table_name, pi.name, filename);
} else if (mode == SET_DENY) {
ret = asprintf(&query, "INSERT INTO %s VALUES (\'%s\', \'%s\', FALSE);",
table_name, pi.name, filename);
} else {
return 1;
}
if (ret < 0) { if (ret < 0) {
// If asprintf fails, the contents of query are undefined (see man // If asprintf fails, the contents of query are undefined (see man

View File

@ -11,6 +11,7 @@
#include "access_t.h" #include "access_t.h"
#include "process_info.h" #include "process_info.h"
#include "set_mode_t.h"
/** /**
* Initializes the permanent permissions table. * Initializes the permanent permissions table.
@ -40,8 +41,11 @@ access_t check_perm_access(const char *filename, struct process_info pi);
* *
* @param filename: The file that the process is trying to access * @param filename: The file that the process is trying to access
* @param pi: The process information * @param pi: The process information
* @param mode: Kind of access rule to be set - SET_DENY to deny access, and
* SET_ALLOW to allow access.
* @return: 0 on success, -1 on failure * @return: 0 on success, -1 on failure
*/ */
int give_perm_access(const char *filename, struct process_info pi); int set_perm_access(const char *filename, struct process_info pi,
set_mode_t mode);
#endif // #ifdef PERM_PERMISSION_TABLE_H #endif // #ifdef PERM_PERMISSION_TABLE_H

5
src/set_mode_t.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef SET_MODE_T_H
#define SET_MODE_T_H
typedef enum { SET_DENY, SET_ALLOW } set_mode_t;
#endif // !SET_MODE_T_H

View File

@ -4,6 +4,7 @@
#include "access_t.h" #include "access_t.h"
#include "process_info.h" #include "process_info.h"
#include "set_mode_t.h"
/** /**
* Initializes the temporary permissions table. * Initializes the temporary permissions table.
@ -35,8 +36,6 @@ void destroy_temp_permissions_table(void);
*/ */
access_t check_temp_access(const char *filename, struct process_info pi); access_t check_temp_access(const char *filename, struct process_info pi);
typedef enum { SET_DENY, SET_ALLOW } set_mode_t;
/** /**
* Sets temporary access mode of the process to the file. * Sets temporary access mode of the process to the file.
* *

View File

@ -49,7 +49,7 @@ int init_ui_socket(const char *perm_permissions_db_filename) {
} }
// Test if Zenity is installed (get version) // Test if Zenity is installed (get version)
fp = popen("zenity --version", "r"); fp = popen("icfs_dialogue --version", "r");
if (fp == NULL) { if (fp == NULL) {
perror("Pipe returned an error"); perror("Pipe returned an error");
return 1; return 1;
@ -77,7 +77,7 @@ struct dialogue_response ask_access(const char *filename,
struct process_info proc_info) { struct process_info proc_info) {
FILE *fp = NULL; FILE *fp = NULL;
char *command = NULL; char *command = NULL;
int ret = asprintf(&command, "zenity \"%d\" \"%s\" \"%s\" \"%s\"", int ret = asprintf(&command, "icfs_dialogue \"%d\" \"%s\" \"%s\" \"%s\"",
proc_info.PID, proc_info.name, get_mountpoint(), filename); proc_info.PID, proc_info.name, get_mountpoint(), filename);
struct dialogue_response response; struct dialogue_response response;
@ -142,8 +142,10 @@ struct dialogue_response ask_access(const char *filename,
response.decision = ALLOW; response.decision = ALLOW;
} else if (zenity_exit_code == ZENITY_YES) { } else if (zenity_exit_code == ZENITY_YES) {
response.decision = ALLOW_TEMP; response.decision = ALLOW_TEMP;
} else { } else if (zenity_exit_code == (ZENITY_NO | ZENITY_PERM)) {
response.decision = DENY; response.decision = DENY;
} else {
response.decision = DENY_TEMP;
} }
return response; return response;
@ -205,7 +207,7 @@ int interactive_access(const char *filename, struct process_info proc_info,
if (opts & GRANT_PERM) { if (opts & GRANT_PERM) {
fprintf(stderr, "Permission granted permanently to %s.\n", proc_info.name); fprintf(stderr, "Permission granted permanently to %s.\n", proc_info.name);
give_perm_access(real_path, proc_info); set_perm_access(real_path, proc_info, SET_ALLOW);
free(real_path); free(real_path);
return 1; return 1;
} }
@ -241,7 +243,7 @@ int interactive_access(const char *filename, struct process_info proc_info,
fprintf(stderr, fprintf(stderr,
"Permission granted permanently to %s based on zenty response.\n", "Permission granted permanently to %s based on zenty response.\n",
proc_info.name); proc_info.name);
give_perm_access(real_path, proc_info); set_perm_access(real_path, proc_info, SET_ALLOW);
free(real_path); free(real_path);
return 1; return 1;
} }
@ -255,7 +257,7 @@ int interactive_access(const char *filename, struct process_info proc_info,
return 1; return 1;
} }
if (response.decision == DENY) { if (response.decision == DENY_TEMP) {
fprintf(stderr, fprintf(stderr,
"Permission denied temporarily to %s based on zenty response.\n", "Permission denied temporarily to %s based on zenty response.\n",
proc_info.name); proc_info.name);
@ -264,6 +266,15 @@ int interactive_access(const char *filename, struct process_info proc_info,
return 0; return 0;
} }
if (response.decision == DENY) {
fprintf(stderr,
"Permission denied permanently to %s based on zenty response.\n",
proc_info.name);
set_perm_access(real_path, proc_info, SET_DENY);
free(real_path);
return 0;
}
free(real_path); free(real_path);
// deny on unknown options. // deny on unknown options.
return 0; return 0;

View File

@ -14,12 +14,14 @@ else
FAKE_ZENITY_RESPONSE=$(cat ~/.fake_zenity_response) FAKE_ZENITY_RESPONSE=$(cat ~/.fake_zenity_response)
printf "%s" "$4" printf "%s" "$4"
if [[ $FAKE_ZENITY_RESPONSE == "yes_tmp" ]]; then if [[ $FAKE_ZENITY_RESPONSE == "yes" ]]; then
exit "$ZENITY_YES" exit "$ZENITY_YES"
elif [[ $FAKE_ZENITY_RESPONSE == "no" ]]; then elif [[ $FAKE_ZENITY_RESPONSE == "no" ]]; then
exit "$ZENITY_NO" exit "$ZENITY_NO"
elif [[ $FAKE_ZENITY_RESPONSE == "yes" ]]; then elif [[ $FAKE_ZENITY_RESPONSE == "yes_perm" ]]; then
exit "$((ZENITY_YES | ZENITY_PERM))" exit "$((ZENITY_YES | ZENITY_PERM))"
elif [[ $FAKE_ZENITY_RESPONSE == "no_perm" ]]; then
exit "$((ZENITY_NO | ZENITY_PERM))"
fi fi
fi fi
fi fi

81
test/opener/Makefile Normal file
View File

@ -0,0 +1,81 @@
SHELL=/bin/bash
# configurable options
ifndef ($(SOURCES_DIR))
SOURCES_DIR := .
endif
ifndef ($(TESTS_DIR))
TESTS_DIR := .
endif
ifndef ($(BUILD_DIR))
BUILD_DIR := .
endif
CC := gcc
CXX := g++
NAME := opener
# dependencies
PACKAGE_NAMES :=
ifeq ($(TEST), 1)
# PACKAGE_NAMES += check # TODO: use check?
endif
# set up cflags and libs
CFLAGS :=
LDFLAGS :=
ifneq ($(PACKAGE_NAMES),)
CFLAGS += $(shell pkg-config --cflags $(PACKAGE_NAMES))
LDFLAGS += $(shell pkg-config --libs $(PACKAGE_NAMES))
endif
ifeq ($(DEBUG),1)
CFLAGS += -O0 -pedantic -g -Wall -Wextra -Wcast-align \
-Wcast-qual -Wdisabled-optimization -Wformat=2 \
-Winit-self -Wlogical-op -Wmissing-declarations \
-Wmissing-include-dirs -Wredundant-decls -Wshadow \
-Wsign-conversion -Wstrict-overflow=5 \
-Wswitch-default -Wundef -Wno-unused
LDFLAGS +=
else
CFLAGS += -O3
LDFLAGS +=
endif
# set up targets
TARGETS := $(BUILD_DIR)/$(NAME)
ifeq ($(TEST), 1)
TARGETS += $(NAME)_test
endif
# build!
default: $(TARGETS)
.PHONY: clean $(NAME)_test
$(NAME)_test: $(BUILD_DIR)/$(NAME)
echo "No tests defined."
#$(BUILD_DIR)/$(NAME)
$(BUILD_DIR)/$(NAME): $(BUILD_DIR)/$(NAME).o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $(BUILD_DIR)/$(NAME)
$(BUILD_DIR)/$(NAME).o: $(SOURCES_DIR)/$(NAME).c
$(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $(BUILD_DIR)/$(NAME).o
clean:
rm $(BUILD_DIR)/*.o $(BUILD_DIR)/$(NAME)

20
test/opener/opener.c Normal file
View File

@ -0,0 +1,20 @@
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: ./opener [FILENAME]");
return 1;
}
int fd = open(argv[1], O_RDWR | O_CREAT);
if (fd == -1) {
perror("Cannot open file");
return 1;
}
close(fd);
return 0;
}

View File

@ -9,6 +9,17 @@ touch ./protected/do-not-remove ./protected/should-be-removed ./protected/truth
chmod 777 ./protected/perm777 ./protected/perm000 chmod 777 ./protected/perm777 ./protected/perm000
echo "Free code, free world." >./protected/motto echo "Free code, free world." >./protected/motto
rm -rf ./openers
mkdir openers
make -C ./opener || (
echo "Could not make the opener program."
exit 1
)
for i in {1..10}; do
cp ./opener/opener "./openers/opener$i"
ln --symbolic "$(realpath "./openers/opener$i")" "./openers/symlinked_opener$i"
done
# set up the fake-zenity # set up the fake-zenity
PATH="$(realpath ./mock/):$PATH" PATH="$(realpath ./mock/):$PATH"
@ -28,6 +39,7 @@ if [[ $1 == "--setuid" ]]; then
else else
echo "Database protection will not be tested due to the lack of setuid capabilites." echo "Database protection will not be tested due to the lack of setuid capabilites."
echo "To test it, run this script with '--setuid'." echo "To test it, run this script with '--setuid'."
#valgrind --leak-check=full -s ../build/icfs -o default_permissions -o debug ./protected ./.pt.db 2>&1 | grep "==\|zenity\|Permission\|column\|callback" &
valgrind --leak-check=full -s ../build/icfs -o default_permissions ./protected ./.pt.db & valgrind --leak-check=full -s ../build/icfs -o default_permissions ./protected ./.pt.db &
sleep 5 sleep 5
fi fi
@ -39,85 +51,105 @@ fi
# create files # create files
zenity --set-fake-response no icfs_dialogue --set-fake-response no
truncate -s 0 ./protected/should-exist-anyway 2>/dev/null && truncate -s 0 ./protected/should-exist-anyway 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: truncate cannot create protected/should-exist despite access being permitted!" # OK echo "[ICFS-TEST]: truncate cannot create protected/should-exist despite access being permitted!" # OK
zenity --set-fake-response yes_tmp icfs_dialogue --set-fake-response yes
truncate -s 0 ./protected/should-exist 2>/dev/null && truncate -s 0 ./protected/should-exist 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: truncate cannot create protected/should-exist despite access being permitted!" # OK echo "[ICFS-TEST]: truncate cannot create protected/should-exist despite access being permitted!" # OK
# write to files # write to files
zenity --set-fake-response no icfs_dialogue --set-fake-response no
sed -e 'a\'"Linux is a cancer that attaches itself in an intellectual property sense to everything it touches." "./protected/truth" 2>/dev/null && sed -e 'a\'"Linux is a cancer that attaches itself in an intellectual property sense to everything it touches." "./protected/truth" 2>/dev/null &&
echo "[ICFS-TEST]: echo can write to protected/lie despite access being denied!" || echo "[ICFS-TEST]: echo can write to protected/lie despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp icfs_dialogue --set-fake-response yes
sed -e 'a\'"Sharing knowledge is the most fundamental act of friendship. Because it is a way you can give something without loosing something." "./protected/truth" 2>/dev/null && sed -e 'a\'"Sharing knowledge is the most fundamental act of friendship. Because it is a way you can give something without loosing something." "./protected/truth" 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: echo cannot write to protected/truth despite access being permitted!" # OK echo "[ICFS-TEST]: echo cannot write to protected/truth despite access being permitted!" # OK
# Read files # Read files
zenity --set-fake-response no icfs_dialogue --set-fake-response no
cat ./protected/motto >/dev/null 2>/dev/null && cat ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: cat can read protected/this-only despite access being denied!" || echo "[ICFS-TEST]: cat can read protected/this-only despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp icfs_dialogue --set-fake-response yes
cat ./protected/motto >/dev/null 2>/dev/null && cat ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: echo cannot create protected/this-only despite access being permitted!" # "Free code, free world." echo "[ICFS-TEST]: echo cannot create protected/this-only despite access being permitted!" # "Free code, free world."
# remove files # remove files
zenity --set-fake-response no icfs_dialogue --set-fake-response no
rm ./protected/do-not-remove >/dev/null 2>/dev/null && rm ./protected/do-not-remove >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: rm can unlink protected/do-not-remove despite access being denied!" || echo "[ICFS-TEST]: rm can unlink protected/do-not-remove despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp icfs_dialogue --set-fake-response yes
rm ./protected/should-be-removed >/dev/null 2>/dev/null && rm ./protected/should-be-removed >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: rm cannot unlink protected/should-be-removed despite access being permitted!" # OK echo "[ICFS-TEST]: rm cannot unlink protected/should-be-removed despite access being permitted!" # OK
# rename files # rename files
zenity --set-fake-response no icfs_dialogue --set-fake-response no
mv ./protected/do-not-rename ./protected/terrible-name 2>/dev/null && mv ./protected/do-not-rename ./protected/terrible-name 2>/dev/null &&
echo "[ICFS-TEST]: mv can rename protected/truth despite access being denied!" || echo "[ICFS-TEST]: mv can rename protected/truth despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp icfs_dialogue --set-fake-response yes
mv ./protected/should-be-renamed ./protected/great-name 2>/dev/null && mv ./protected/should-be-renamed ./protected/great-name 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: mv cannot rename should-be-removed to renamed-file despite access being permitted!" # OK echo "[ICFS-TEST]: mv cannot rename should-be-removed to renamed-file despite access being permitted!" # OK
# change permissions # change permissions
zenity --set-fake-response no icfs_dialogue --set-fake-response no
chmod 000 ./protected/perm777 2>/dev/null && chmod 000 ./protected/perm777 2>/dev/null &&
echo "[ICFS-TEST]: chmod can change permissions of protected/perm777 despite access being denied!" || echo "[ICFS-TEST]: chmod can change permissions of protected/perm777 despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp icfs_dialogue --set-fake-response yes
chmod 000 ./protected/perm000 2>/dev/null && chmod 000 ./protected/perm000 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: chmod cannot change permissions of protected/perm000 despite access being permitted!" # OK echo "[ICFS-TEST]: chmod cannot change permissions of protected/perm000 despite access being permitted!" # OK
# test permanent permissions # test permanent permissions
zenity --set-fake-response yes icfs_dialogue --set-fake-response yes_perm
cat ./protected/motto >/dev/null 2>/dev/null && openers/opener1 ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: echo cannot read protected/motto despite access being permitted!" # OK echo "[ICFS-TEST]: openers/opener1 cannot read protected/motto despite access being permitted!" # OK
zenity --set-fake-response no # this should be ignored icfs_dialogue --set-fake-response no # this should be ignored
cat ./protected/motto >/dev/null 2>/dev/null && openers/opener1 ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" || echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: echo cannot read protected/motto despite access being permitted!" # OK echo "[ICFS-TEST]: openers/opener1 cannot read protected/motto despite access being permitted!" # OK
icfs_dialogue --set-fake-response no # this should be ignored
openers/symlinked_opener1 ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: openers/symlinked_opener1 cannot read protected/motto despite access being permitted!" # OK
icfs_dialogue --set-fake-response no_perm
openers/opener2 ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: openers/opener2 can read protected/motto despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
icfs_dialogue --set-fake-response yes # this should be ignored
openers/opener2 ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: openers/opener2 can read protected/motto despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
icfs_dialogue --set-fake-response yes # this should be ignored
openers/symlinked_opener2 ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: openers/symlinked_opener2 can read protected/motto despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
# test database access # test database access
if [[ -r "./.pt.db" || -w "./.pt.db" ]]; then if [[ -r "./.pt.db" || -w "./.pt.db" ]]; then