ICFS/src/ui-socket.c
2025-05-06 12:17:26 +02:00

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;
}