281 lines
7.7 KiB
C
281 lines
7.7 KiB
C
/*
|
|
ICFS: Interactively Controlled File System
|
|
Copyright (C) 2024-2025 Fedir Kovalov
|
|
|
|
This program can be distributed under the terms of the GNU GPLv2.
|
|
See the file LICENSE.
|
|
*/
|
|
|
|
#include "access_t.h"
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#define _GNU_SOURCE
|
|
#include "cc.h"
|
|
#include "perm_permissions_table.h"
|
|
#include "real_filename.h"
|
|
#include "sourcefs.h"
|
|
#include "temp_permissions_table.h"
|
|
#include "ui-socket.h"
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
|
|
#define DIALOGUE_YES 0
|
|
#define DIALOGUE_NO 1
|
|
#define DIALOGUE_PERM 2
|
|
|
|
struct dialogue_response {
|
|
access_t decision;
|
|
char *filename;
|
|
};
|
|
|
|
int init_ui_socket(const char *perm_permissions_db_filename) {
|
|
FILE *fp = NULL;
|
|
|
|
if (init_temp_permissions_table()) {
|
|
fprintf(stderr, "Could not initialize temporary permissions table.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (init_perm_permissions_table(perm_permissions_db_filename)) {
|
|
fprintf(stderr, "Could not initialize permanent permissions table.\n");
|
|
return 1;
|
|
}
|
|
|
|
// Test if dialogue is installed (get version)
|
|
fp = popen("icfs_dialogue --version", "r");
|
|
if (fp == NULL) {
|
|
perror("Pipe returned an error");
|
|
return 1;
|
|
}
|
|
|
|
pclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
void destroy_ui_socket(void) {
|
|
destroy_temp_permissions_table();
|
|
destroy_perm_permissions_table();
|
|
}
|
|
|
|
/**
|
|
* Asks the user if the process should be allowed to access the file using the
|
|
* GUI
|
|
*
|
|
* @param filename: The file that the process is trying to access
|
|
* @param pi: The process information
|
|
* @return: access status - ALLOW, DENY or ALLOW_TEMP
|
|
* allowed for the runtime of the process
|
|
*/
|
|
struct dialogue_response ask_access(const char *filename,
|
|
struct process_info proc_info) {
|
|
FILE *fp = NULL;
|
|
char *command = NULL;
|
|
int ret = asprintf(&command, "icfs_dialogue \"%d\" \"%s\" \"%s\" \"%s\"",
|
|
proc_info.PID, proc_info.name, get_mountpoint(), filename);
|
|
|
|
struct dialogue_response response;
|
|
response.decision = DENY;
|
|
response.filename = NULL;
|
|
|
|
if (ret < 0) {
|
|
// If asprintf fails, the contents of command are undefined (see man
|
|
// asprintf). That does not explicitly rule out that command will be a valid
|
|
// pointer. But the risk of freeing a non-allocated pointer is too much to
|
|
// justify preparing for this.
|
|
fprintf(stderr, "Could not create query on rule insertion");
|
|
perror("");
|
|
response.decision = DENY;
|
|
response.filename = malloc(2);
|
|
response.filename[0] = '/';
|
|
response.filename[1] = 0;
|
|
return response;
|
|
}
|
|
|
|
// dialogue Question Message Popup
|
|
fp = popen(command, "r");
|
|
free(command);
|
|
|
|
if (fp == NULL) {
|
|
perror("Pipe returned a error");
|
|
response.decision = DENY;
|
|
response.filename = malloc(2);
|
|
response.filename[0] = '/';
|
|
response.filename[1] = 0;
|
|
return response;
|
|
}
|
|
|
|
str(char) dialogue_output;
|
|
init(&dialogue_output);
|
|
|
|
char line[1024]; // Buffer to read individual lines
|
|
|
|
// Read the command output line by line
|
|
while (fgets(line, sizeof(line), fp)) {
|
|
push_fmt(&dialogue_output, line);
|
|
}
|
|
|
|
int dialogue_exit_code = WEXITSTATUS(pclose(fp));
|
|
fprintf(stderr, "dialogue wrote out %s\n", first(&dialogue_output));
|
|
fprintf(stderr, "dialogue returned %d\n", dialogue_exit_code);
|
|
|
|
if (size(&dialogue_output) == 0) {
|
|
push(&dialogue_output, '/');
|
|
}
|
|
|
|
assert(strlen(first(&dialogue_output)) == size(&dialogue_output));
|
|
|
|
response.filename = malloc(size(&dialogue_output) + 1);
|
|
strcpy(response.filename, first(&dialogue_output));
|
|
// response.filename[size(&dialogue_output)] = 0;
|
|
|
|
// assert(0 == strcmp(response.filename, first(&dialogue_output)));
|
|
cleanup(&dialogue_output);
|
|
|
|
if (dialogue_exit_code == (DIALOGUE_YES | DIALOGUE_PERM)) {
|
|
response.decision = ALLOW;
|
|
} else if (dialogue_exit_code == DIALOGUE_YES) {
|
|
response.decision = ALLOW_TEMP;
|
|
} else if (dialogue_exit_code == (DIALOGUE_NO | DIALOGUE_PERM)) {
|
|
response.decision = DENY;
|
|
} else {
|
|
response.decision = DENY_TEMP;
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Check access according to:
|
|
* 1. temp permission table
|
|
* 2. permanent permission table
|
|
* 3. user descision
|
|
*
|
|
* @param filename: The file that the process is trying to access
|
|
* @pram pi: The process information
|
|
* @param opts: options (GRANT_TEMP, GRANT_PERM)
|
|
* @return: 0 if access is denied, 1 if access is allowed
|
|
*/
|
|
int interactive_access(const char *filename, struct process_info proc_info,
|
|
int opts) {
|
|
char *real_path = real_filename(filename);
|
|
|
|
access_t access = check_temp_access(real_path, proc_info);
|
|
if (access == ALLOW) {
|
|
fprintf(stderr,
|
|
"Permission allowed to %s based on a rule present in the temp "
|
|
"permission table.\n",
|
|
proc_info.name);
|
|
free(real_path);
|
|
return 1;
|
|
}
|
|
if (access == DENY) {
|
|
fprintf(stderr,
|
|
"Permission denied to %s based on a rule present in the temp "
|
|
"permission table.\n",
|
|
proc_info.name);
|
|
free(real_path);
|
|
return 0;
|
|
}
|
|
|
|
access = check_perm_access(real_path, proc_info);
|
|
if (access == ALLOW) {
|
|
fprintf(stderr,
|
|
"Permission allowed to %s based on a rule present in the perm "
|
|
"permission table.\n",
|
|
proc_info.name);
|
|
free(real_path);
|
|
return 1;
|
|
}
|
|
if (access == DENY) {
|
|
fprintf(stderr,
|
|
"Permission denied to %s based on a rule present in the perm "
|
|
"permission table.\n",
|
|
proc_info.name);
|
|
free(real_path);
|
|
return 0;
|
|
}
|
|
|
|
// if noth GRANT_TEMP and GRANT_PERM are selected, then only permanent
|
|
// permissions are granted
|
|
|
|
if (opts & GRANT_PERM) {
|
|
fprintf(stderr, "Permission granted permanently to %s.\n", proc_info.name);
|
|
set_perm_access(real_path, proc_info, SET_ALLOW);
|
|
free(real_path);
|
|
return 1;
|
|
}
|
|
if (opts & GRANT_TEMP) {
|
|
fprintf(stderr, "Permission granted temporarily to %s.\n", proc_info.name);
|
|
set_temp_access(real_path, proc_info, SET_ALLOW);
|
|
free(real_path);
|
|
return 1;
|
|
}
|
|
|
|
struct dialogue_response response = ask_access(filename, proc_info);
|
|
// fprintf(stderr, "%s", response.filename);
|
|
// assert(0 != strlen(response.filename));
|
|
|
|
// the user might specify a different file in the dialogue, so we need to
|
|
// check if it is valid
|
|
|
|
while (source_access(response.filename, F_OK)) {
|
|
// if it is invalid, just ask again.
|
|
fprintf(stderr, "Filename returned by zenty wasn't correct: %s\n",
|
|
response.filename);
|
|
free(response.filename);
|
|
response = ask_access(filename, proc_info);
|
|
}
|
|
|
|
free(real_path);
|
|
|
|
real_path = real_filename(response.filename);
|
|
free(response.filename);
|
|
|
|
if (response.decision == ALLOW) {
|
|
fprintf(stderr,
|
|
"Permission granted permanently to %s based on zenty response.\n",
|
|
proc_info.name);
|
|
set_perm_access(real_path, proc_info, SET_ALLOW);
|
|
free(real_path);
|
|
return 1;
|
|
}
|
|
|
|
if (response.decision == ALLOW_TEMP) {
|
|
fprintf(stderr,
|
|
"Permission granted temporarily to %s based on zenty response.\n",
|
|
proc_info.name);
|
|
set_temp_access(real_path, proc_info, SET_ALLOW);
|
|
free(real_path);
|
|
return 1;
|
|
}
|
|
|
|
if (response.decision == DENY_TEMP) {
|
|
fprintf(stderr,
|
|
"Permission denied temporarily to %s based on zenty response.\n",
|
|
proc_info.name);
|
|
set_temp_access(real_path, proc_info, SET_DENY);
|
|
free(real_path);
|
|
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);
|
|
// deny on unknown options.
|
|
return 0;
|
|
}
|