/* 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 "perm_permissions_table.h" #include "process_info.h" #include #include #include #include #include #include #include #include #include #include #include sqlite3 *perm_database = NULL; const char *const table_name = "permissions"; // one row corresponds to a permission to access one file for one executable const int column_count = 2; const char *const schema[] = {"executable", "filename"}; const char *const types[] = {"TEXT", "TEXT"}; uid_t ruid, euid, current_pid; pthread_mutex_t uid_switch = PTHREAD_MUTEX_INITIALIZER; void set_db_fsuid() { pthread_mutex_lock(&uid_switch); if (current_pid == ruid) return; int status = -1; status = setfsuid(ruid); if (status < 0) { fprintf(stderr, "Couldn't set uid to %d.\n", ruid); exit(status); } pthread_mutex_unlock(&uid_switch); } void set_real_fsuid() { pthread_mutex_lock(&uid_switch); if (current_pid == ruid) return; int status = -1; status = setfsuid(ruid); if (status < 0) { fprintf(stderr, "Couldn't set uid to %d.\n", euid); exit(status); } pthread_mutex_unlock(&uid_switch); } static int check_table_col_schema(void *notused, int argc, char **argv, char **colname) { (void)notused; (void)colname; if (argc < 3) { fprintf(stderr, "Unexpected amount of arguments given to the callback.\n"); return 1; } int column_num = atoi(argv[0]); if (column_num >= column_count) { fprintf(stderr, "Table contains more columns than expected.\n"); return 1; } if (strcmp(schema[column_num], argv[1]) == 0 && strcmp(types[column_num], argv[2]) == 0) { return 0; } fprintf(stderr, "Column %d does not conform to the schema.\n", column_num); return 1; } static int set_flag(void *flag, int argc, char **argv, char **colname) { (void)argc; (void)argv; (void)colname; *(int *)flag = 1; return 0; } int create_database_schema() { fprintf(stderr, "Creating table 'permissions'.\n"); const char *create_query = "CREATE TABLE permissions(executable TEXT NOT " "NULL, filename TEXT NOT NULL);"; char *err = NULL; int ret = sqlite3_exec(perm_database, create_query, NULL, NULL, &err); if (ret != SQLITE_OK) { fprintf(stderr, "sqlite3 error: %s\n", err); sqlite3_free(err); return 1; } fprintf(stderr, "Database created successfully\n"); return 0; } /** * Ensures that the database schema is correct. * * @return: 0 if the schema is correct, 1 if the schema could not be corrected. */ int ensure_database_schema() { // Check for the table. int result = sqlite3_table_column_metadata( perm_database, NULL, table_name, NULL, NULL, NULL, NULL, NULL, NULL); if (result == SQLITE_ERROR) { fprintf(stderr, "Table '%s' does not exist.\n", table_name); if (create_database_schema()) { fprintf(stderr, "Table could not be created.\n"); return 1; } return 0; } else if (result != SQLITE_OK) { fprintf(stderr, "Database metadata could not be retrieved.\n"); return 1; } const char *pragma = "PRAGMA table_info(permissions);"; char *err = NULL; int ret = sqlite3_exec(perm_database, pragma, check_table_col_schema, NULL, &err); if (ret != SQLITE_OK) { fprintf(stderr, "sqlite3 error: %s\n", err); sqlite3_free(err); return 1; } fprintf(stderr, "Schema is correct.\n"); return 0; } /** * Initializes the permanent permissions table. * * @param db_filename: The filename of the permissions sqlite3 database * @return: 0 on success, -1 on failure */ int init_perm_permissions_table(const char *db_filename) { // we don't want the group and others to access the db umask(0077); ruid = getuid(); euid = geteuid(); fprintf(stderr, "Running with uid: %d, gid: %d\n", euid, getegid()); if (sqlite3_open(db_filename, &perm_database)) { perror("Can't open permanent permissions database:"); return -1; } umask(0); if (ensure_database_schema()) { fprintf(stderr, "Database schema is not correct.\n"); return -1; } int status = seteuid(ruid); if (status < 0) { fprintf(stderr, "Couldn't set euid to ruid during database setup.\n"); exit(status); } return 0; } /** * Destroys the permanent permissions table. */ void destroy_perm_permissions_table() { sqlite3_close(perm_database); } /** * Checks if the process has a permanent access to the file. * * @param filename: The file that the process is trying to access * @pram pi: The process information * @return: 0 if access is denied, 1 if access is allowed */ int check_perm_access(const char *filename, struct process_info pi) { size_t query_len = 56 + strlen(table_name) + strlen(filename) + strlen(pi.name); const char *query = malloc(query_len); size_t should_be_written = snprintf( query, query_len, "SELECT * FROM %s WHERE executable = \'%s\' AND filename = \'%s\';", table_name, pi.name, filename); // -1 for the \0 if (should_be_written != query_len - 1) { fprintf(stderr, "Unexpected query size while permanent access rule check: " "Expected %lu, but snprintf returned %lu. The query: %s\n", query_len, should_be_written, query); return 0; } char *sqlite_error = NULL; int flag = 0; int ret = sqlite3_exec(perm_database, query, set_flag, &flag, &sqlite_error); if (ret != SQLITE_OK) { fprintf(stderr, "SQLite returned an error: %s\n", sqlite_error); sqlite3_free(sqlite_error); free(query); return 0; } free(query); return flag; } /** * Gives permanent access to the process to the file. * * @param filename: The file that the process is trying to access * @param pi: The process information * @return: 0 on success, 1 on failure */ int give_perm_access(const char *filename, struct process_info pi) { size_t query_len = 30 + strlen(table_name) + strlen(filename) + strlen(pi.name); const char *query = malloc(query_len); size_t should_be_written = snprintf(query, query_len, "INSERT INTO %s VALUES (\'%s\', \'%s\');", table_name, pi.name, filename); // -1 for the \0 if (should_be_written != query_len - 1) { fprintf(stderr, "Unexpected query size while permanent access rule insertion: " "Expected %lu, but snprintf returned %lu\n", query_len, should_be_written); return 1; } char *sqlite_error = NULL; int ret = sqlite3_exec(perm_database, query, NULL, NULL, &sqlite_error); if (ret != SQLITE_OK) { fprintf(stderr, "SQLite returned an error: %s\n", sqlite_error); sqlite3_free(sqlite_error); free(query); return 1; } free(query); return 0; }