/* 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 #include #include #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 #include #include #include #include #include #include #include #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; }