static void expand_path(OPERATION op, const char *path, const char *fname, const char *emptydir, const char *emptyfile) { assert(path); assert(fname); assert(emptydir); assert(emptyfile); char newname[strlen(path) + strlen(fname) + 1]; sprintf(newname, "%s%s", path, fname); globbing(op, newname, emptydir, emptyfile); }
// blacklist files or directoies by mounting empty files on top of them void fs_blacklist(void) { char *homedir = cfg.homedir; assert(homedir); ProfileEntry *entry = cfg.profile; if (!entry) return; size_t noblacklist_c = 0; size_t noblacklist_m = 32; char **noblacklist = calloc(noblacklist_m, sizeof(*noblacklist)); if (noblacklist == NULL) errExit("failed allocating memory for noblacklist entries"); while (entry) { OPERATION op = OPERATION_MAX; char *ptr; // whitelist commands handled by fs_whitelist() if (strncmp(entry->data, "whitelist ", 10) == 0 || *entry->data == '\0') { entry = entry->next; continue; } // process bind command if (strncmp(entry->data, "bind ", 5) == 0) { char *dname1 = entry->data + 5; char *dname2 = split_comma(dname1); if (dname2 == NULL) { fprintf(stderr, "Error: second directory missing in bind command\n"); entry = entry->next; continue; } struct stat s; if (stat(dname1, &s) == -1) { fprintf(stderr, "Error: cannot find directories for bind command\n"); entry = entry->next; continue; } if (stat(dname2, &s) == -1) { fprintf(stderr, "Error: cannot find directories for bind command\n"); entry = entry->next; continue; } // mount --bind olddir newdir if (arg_debug) printf("Mount-bind %s on top of %s\n", dname1, dname2); // preserve dname2 mode and ownership if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); /* coverity[toctou] */ if (chown(dname2, s.st_uid, s.st_gid) == -1) errExit("mount-bind chown"); /* coverity[toctou] */ if (chmod(dname2, s.st_mode) == -1) errExit("mount-bind chmod"); entry = entry->next; continue; } // Process noblacklist command if (strncmp(entry->data, "noblacklist ", 12) == 0) { if (noblacklist_c >= noblacklist_m) { noblacklist_m *= 2; noblacklist = realloc(noblacklist, sizeof(*noblacklist) * noblacklist_m); if (noblacklist == NULL) errExit("failed increasing memory for noblacklist entries"); } else noblacklist[noblacklist_c++] = expand_home(entry->data + 12, homedir); entry = entry->next; continue; } // process blacklist command if (strncmp(entry->data, "blacklist ", 10) == 0) { ptr = entry->data + 10; op = BLACKLIST_FILE; } else if (strncmp(entry->data, "blacklist-nolog ", 16) == 0) { ptr = entry->data + 16; op = BLACKLIST_NOLOG; } else if (strncmp(entry->data, "read-only ", 10) == 0) { ptr = entry->data + 10; op = MOUNT_READONLY; } else if (strncmp(entry->data, "tmpfs ", 6) == 0) { ptr = entry->data + 6; op = MOUNT_TMPFS; } else { fprintf(stderr, "Error: invalid profile line %s\n", entry->data); entry = entry->next; continue; } // replace home macro in blacklist array char *new_name = expand_home(ptr, homedir); ptr = new_name; // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories if (ptr) { if (strncmp(ptr, "${PATH}", 7) == 0) { char *fname = ptr + 7; size_t fname_len = strlen(fname); char **paths = build_paths(); //{"/bin", "/sbin", "/usr/bin", "/usr/sbin", NULL}; int i = 0; while (paths[i] != NULL) { char *path = paths[i]; i++; char newname[strlen(path) + fname_len + 1]; sprintf(newname, "%s%s", path, fname); globbing(op, newname, (const char**)noblacklist, noblacklist_c); } } else globbing(op, ptr, (const char**)noblacklist, noblacklist_c); } if (new_name) free(new_name); entry = entry->next; } size_t i; for (i = 0; i < noblacklist_c; i++) free(noblacklist[i]); free(noblacklist); }
// blacklist files or directoies by mounting empty files on top of them void fs_blacklist(const char *homedir) { ProfileEntry *entry = cfg.profile; if (!entry) return; char *emptydir = create_empty_dir(); char *emptyfile = create_empty_file(); while (entry) { OPERATION op = OPERATION_MAX; char *ptr; // process blacklist command if (strncmp(entry->data, "bind", 4) == 0) { char *dname1 = entry->data + 5; char *dname2 = split_comma(dname1); if (dname2 == NULL) { fprintf(stderr, "Error: second directory missing in bind command\n"); entry = entry->next; continue; } struct stat s; if (stat(dname1, &s) == -1) { fprintf(stderr, "Error: cannot find directories for bind command\n"); entry = entry->next; continue; } if (stat(dname2, &s) == -1) { fprintf(stderr, "Error: cannot find directories for bind command\n"); entry = entry->next; continue; } // mount --bind olddir newdir if (arg_debug) printf("Mount-bind %s on top of %s\n", dname1, dname2); // preserve dname2 mode and ownership if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); /* coverity[toctou] */ if (chown(dname2, s.st_uid, s.st_gid) == -1) errExit("mount-bind chown"); /* coverity[toctou] */ if (chmod(dname2, s.st_mode) == -1) errExit("mount-bind chmod"); entry = entry->next; continue; } // process blacklist command if (strncmp(entry->data, "blacklist", 9) == 0) { ptr = entry->data + 10; op = BLACKLIST_FILE; } else if (strncmp(entry->data, "read-only", 9) == 0) { ptr = entry->data + 10; op = MOUNT_READONLY; } else if (strncmp(entry->data, "tmpfs", 5) == 0) { ptr = entry->data + 6; op = MOUNT_TMPFS; } else { fprintf(stderr, "Error: invalid profile line %s\n", entry->data); entry = entry->next; continue; } // replace home macro in blacklist array char *new_name = NULL; if (strncmp(ptr, "${HOME}", 7) == 0) { if (asprintf(&new_name, "%s%s", homedir, ptr + 7) == -1) errExit("asprintf"); ptr = new_name; } // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories if (strncmp(ptr, "${PATH}", 7) == 0) { expand_path(op, "/bin", ptr + 7, emptydir, emptyfile); expand_path(op, "/sbin", ptr + 7, emptydir, emptyfile); expand_path(op, "/usr/bin", ptr + 7, emptydir, emptyfile); expand_path(op, "/usr/sbin", ptr + 7, emptydir, emptyfile); } else globbing(op, ptr, emptydir, emptyfile); if (new_name) free(new_name); entry = entry->next; } }
// blacklist files or directoies by mounting empty files on top of them void fs_blacklist(void) { char *homedir = cfg.homedir; assert(homedir); ProfileEntry *entry = cfg.profile; if (!entry) return; char *emptydir = create_empty_dir(); char *emptyfile = create_empty_file(); // a statically allocated buffer works for all current needs // TODO: if dynamic allocation is ever needed, we should probably add // libraries that make it easy to do without introducing security bugs char *noblacklist[32]; size_t noblacklist_c = 0; while (entry) { OPERATION op = OPERATION_MAX; char *ptr; // whitelist commands handled by fs_whitelist() if (strncmp(entry->data, "whitelist ", 10) == 0 || *entry->data == '\0') { entry = entry->next; continue; } // process bind command if (strncmp(entry->data, "bind ", 5) == 0) { char *dname1 = entry->data + 5; char *dname2 = split_comma(dname1); if (dname2 == NULL) { fprintf(stderr, "Error: second directory missing in bind command\n"); entry = entry->next; continue; } struct stat s; if (stat(dname1, &s) == -1) { fprintf(stderr, "Error: cannot find directories for bind command\n"); entry = entry->next; continue; } if (stat(dname2, &s) == -1) { fprintf(stderr, "Error: cannot find directories for bind command\n"); entry = entry->next; continue; } // mount --bind olddir newdir if (arg_debug) printf("Mount-bind %s on top of %s\n", dname1, dname2); // preserve dname2 mode and ownership if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount bind"); /* coverity[toctou] */ if (chown(dname2, s.st_uid, s.st_gid) == -1) errExit("mount-bind chown"); /* coverity[toctou] */ if (chmod(dname2, s.st_mode) == -1) errExit("mount-bind chmod"); entry = entry->next; continue; } // Process noblacklist command if (strncmp(entry->data, "noblacklist ", 12) == 0) { if (noblacklist_c >= sizeof(noblacklist) / sizeof(noblacklist[0])) { fputs("Error: out of memory for noblacklist entries\n", stderr); exit(1); } else noblacklist[noblacklist_c++] = expand_home(entry->data + 12, homedir); entry = entry->next; continue; } // process blacklist command if (strncmp(entry->data, "blacklist ", 10) == 0) { ptr = entry->data + 10; op = BLACKLIST_FILE; } else if (strncmp(entry->data, "read-only ", 10) == 0) { ptr = entry->data + 10; op = MOUNT_READONLY; } else if (strncmp(entry->data, "tmpfs ", 6) == 0) { ptr = entry->data + 6; op = MOUNT_TMPFS; } else { fprintf(stderr, "Error: invalid profile line %s\n", entry->data); entry = entry->next; continue; } // replace home macro in blacklist array char *new_name = expand_home(ptr, homedir); ptr = new_name; // expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories // TODO: should we look for more bin paths? if (ptr) { if (strncmp(ptr, "${PATH}", 7) == 0) { char *fname = ptr + 7; size_t fname_len = strlen(fname); char **path, *paths[] = {"/bin", "/sbin", "/usr/bin", "/usr/sbin", NULL}; for (path = &paths[0]; *path; path++) { char newname[strlen(*path) + fname_len + 1]; sprintf(newname, "%s%s", *path, fname); globbing(op, newname, (const char**)noblacklist, noblacklist_c, emptydir, emptyfile); } } else globbing(op, ptr, (const char**)noblacklist, noblacklist_c, emptydir, emptyfile); } if (new_name) free(new_name); entry = entry->next; } size_t i; for (i = 0; i < noblacklist_c; i++) free(noblacklist[i]); }