]> git.ipfire.org Git - pakfire.git/commitdiff
FHS: Implement some simple filesystem checks
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 17 Mar 2023 10:00:59 +0000 (10:00 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 17 Mar 2023 10:00:59 +0000 (10:00 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/libpakfire/build.c
src/libpakfire/fhs.c [new file with mode: 0644]
src/libpakfire/include/pakfire/fhs.h [new file with mode: 0644]

index 6d2a0b7b0324c077813a999a8fa52b4c3b1d2b3e..e9b2b2386c93c97c63a2ef0f87fbf0bad1554c4b 100644 (file)
@@ -227,6 +227,7 @@ libpakfire_la_SOURCES = \
        src/libpakfire/digest.c \
        src/libpakfire/dist.c \
        src/libpakfire/downloader.c \
+       src/libpakfire/fhs.c \
        src/libpakfire/file.c \
        src/libpakfire/filelist.c \
        src/libpakfire/jail.c \
@@ -267,6 +268,7 @@ pkginclude_HEADERS += \
        src/libpakfire/include/pakfire/digest.h \
        src/libpakfire/include/pakfire/dist.h \
        src/libpakfire/include/pakfire/downloader.h \
+       src/libpakfire/include/pakfire/fhs.h \
        src/libpakfire/include/pakfire/file.h \
        src/libpakfire/include/pakfire/filelist.h \
        src/libpakfire/include/pakfire/i18n.h \
index e12ce63f0c4899f6edc42b352a753822e215d29a..66bdbd19028969489d525d9c6a0b4b3ed6b0ece0 100644 (file)
@@ -32,6 +32,7 @@
 #include <pakfire/cgroup.h>
 #include <pakfire/dependencies.h>
 #include <pakfire/dist.h>
+#include <pakfire/fhs.h>
 #include <pakfire/file.h>
 #include <pakfire/i18n.h>
 #include <pakfire/jail.h>
@@ -1189,6 +1190,35 @@ static int pakfire_build_post_check_broken_symlinks(
                PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
 }
 
+/*
+       Filesystem Layout Check
+*/
+static int __pakfire_build_post_check_filesystem(
+               struct pakfire* pakfire, struct pakfire_file* file, void* data) {
+       struct pakfire_filelist* illegal = (struct pakfire_filelist*)data;
+       int r;
+
+       // Perform FHS check
+       r = pakfire_fhs_check_file(pakfire, file);
+       if (r) {
+               r = pakfire_filelist_add(illegal, file);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+static int pakfire_build_post_check_filesystem(
+               struct pakfire_build* build, struct pakfire_filelist* filelist) {
+       return pakfire_build_post_process_files(
+               build,
+               filelist,
+               "Illegal files:",
+               __pakfire_build_post_check_filesystem,
+               PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
+}
+
 /*
        Hardening
 */
@@ -1274,6 +1304,11 @@ static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
        if (r)
                goto ERROR;
 
+       // Check filesystem layout
+       r = pakfire_build_post_check_filesystem(build, filelist);
+       if (r)
+               goto ERROR;
+
        // Check hardening
        r = pakfire_build_post_check_hardening(build, filelist);
        if (r)
diff --git a/src/libpakfire/fhs.c b/src/libpakfire/fhs.c
new file mode 100644 (file)
index 0000000..73c258d
--- /dev/null
@@ -0,0 +1,226 @@
+/*#############################################################################
+#                                                                             #
+# Pakfire - The IPFire package management system                              #
+# Copyright (C) 2021 Pakfire development team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <sys/stat.h>
+
+#include <pakfire/fhs.h>
+#include <pakfire/file.h>
+#include <pakfire/logging.h>
+#include <pakfire/pakfire.h>
+#include <pakfire/util.h>
+
+/*
+       This struct defines any FHS checks.
+
+       They are being processed in order from top to bottom which is why we are starting
+       with some more prominent matches and have the less important stuff at the bottom.
+*/
+static const struct pakfire_fhs_check {
+       const char* path;
+       enum pakfire_fhs_check_flags {
+               PAKFIRE_FHS_MUSTNOTEXIST = (1 << 0),
+       } flags;
+       const mode_t mode;
+} pakfire_fhs_check[] = {
+       // /usr
+       { "/usr",         0, S_IFDIR|0755 },
+       { "/usr/bin",     0, S_IFDIR|0755 },
+       { "/usr/include", 0, S_IFDIR|0755 },
+       { "/usr/lib",     0, S_IFDIR|0755 },
+       { "/usr/lib64",   0, S_IFDIR|0755 },
+       { "/usr/sbin",    0, S_IFDIR|0755 },
+       { "/usr/share",   0, S_IFDIR|0755 },
+       { "/usr/src",     0, S_IFDIR|0755 },
+
+       // /var
+       { "/var",         0, S_IFDIR|0755 },
+       { "/var/cache",   0, S_IFDIR|0755 },
+       { "/var/db",      0, S_IFDIR|0755 },
+       { "/var/empty",   0, S_IFDIR|0755 },
+       { "/var/lib",     0, S_IFDIR|0755 },
+       { "/var/log",     0, S_IFDIR|0755 },
+       { "/var/mail",    0, S_IFDIR|0755 },
+       { "/var/opt",     0, S_IFDIR|0755 },
+       { "/var/run",     0, S_IFLNK|0777 },
+       { "/var/spool",   0, S_IFDIR|0755 },
+       { "/var/tmp",     0, S_IFDIR|1755 },
+       { "/var/tmp/**",  PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // /boot
+       { "/boot",        0, S_IFDIR|0755 },
+       { "/boot/efi",    0, S_IFDIR|0755 },
+
+       // /dev (nothing may exist in it)
+       { "/dev",         0, S_IFDIR|0755 },
+       { "/dev/**",      PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // /etc
+       { "/etc",         0, S_IFDIR|0755 },
+
+       // /home
+       { "/home",        0, S_IFDIR|0755 },
+       { "/home/**",     PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // /opt
+       { "/opt",         0, S_IFDIR|0755 },
+       // These directories belong to the "local administrator"
+       // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s13.html
+       { "/opt/bin",     PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+       { "/opt/doc",     PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+       { "/opt/include", PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+       { "/opt/info",    PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+       { "/opt/lib",     PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+       { "/opt/man",     PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // /proc
+       { "/proc",        0, S_IFDIR|0755 },
+       { "/proc/**",     PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // /run
+       { "/run",         0, S_IFDIR|0755 },
+
+       // /sys
+       { "/sys",         0, S_IFDIR|0755 },
+       { "/sys/**",      PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // /tmp
+       { "/tmp",         0, S_IFDIR|1755 },
+       { "/tmp/**",      PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // FHS Directories
+       { "/media",       0, S_IFDIR|0755 },
+       { "/mnt",         0, S_IFDIR|0755 },
+       { "/srv",         0, S_IFDIR|0755 },
+
+       // /bin, /sbin, /lib, and /lib64 have to be symlinks
+       { "/bin",         0, S_IFLNK|0777 },
+       { "/lib",         0, S_IFLNK|0777 },
+       { "/lib64",       0, S_IFLNK|0777 },
+       { "/sbin",        0, S_IFLNK|0777 },
+
+       // There cannot be anything else in /
+       { "/*",           PAKFIRE_FHS_MUSTNOTEXIST, 0 },
+
+       // Catch all so that we won't throw an error
+       { "/**",          0, 0 },
+
+       // Sentinel
+       { NULL },
+};
+
+static const struct pakfire_fhs_check* pakfire_fhs_find_check(
+               struct pakfire* pakfire, const char* path) {
+       const struct pakfire_fhs_check* check = NULL;
+       int r;
+
+       // Walk through all possible checks
+       for (check = pakfire_fhs_check; check->path; check++) {
+               r = pakfire_path_match(check->path, path);
+               switch (r) {
+                       // No match
+                       case 0:
+                               continue;
+
+                       // Match!
+                       case 1:
+                               DEBUG(pakfire, "%s matches check '%s'\n", path, check->path);
+
+                               return check;
+
+                       // Error :(
+                       default:
+                               goto ERROR;
+               }
+       }
+
+ERROR:
+       ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path);
+
+       return NULL;
+}
+
+static int pakfire_fhs_check_mode(struct pakfire* pakfire,
+               const struct pakfire_fhs_check* check, struct pakfire_file* file) {
+       // No mode defined. Skipping check...
+       if (!check->mode)
+               return 0;
+
+       const char* path = pakfire_file_get_path(file);
+
+       // Compare mode
+       const mode_t mode = pakfire_file_get_mode(file);
+
+       // Check if mode matches straight away
+       if (check->mode == mode)
+               return 0;
+
+       const mode_t check_type  = check->mode &  S_IFMT;
+       const mode_t check_perms = check->mode & ~S_IFMT;
+
+       // Check the file type
+       if (check_type) {
+               if ((mode_t)pakfire_file_get_type(file) != check_type) {
+                       ERROR(pakfire, "%s: Filetype does not match\n", path);
+                       return 1;
+               }
+       }
+
+       // Check the file perms
+       if (check_perms) {
+               if (pakfire_file_get_perms(file) != check_perms) {
+                       ERROR(pakfire, "%s: Permissions do not match\n", path);
+                       return 1;
+               }
+       }
+
+       // Check passed
+       return 0;
+}
+
+int pakfire_fhs_check_file(struct pakfire* pakfire, struct pakfire_file* file) {
+       const struct pakfire_fhs_check* check = NULL;
+       int r;
+
+       // Get the file path
+       const char* path = pakfire_file_get_path(file);
+       if (!path)
+               return 1;
+
+       // Find a check
+       check = pakfire_fhs_find_check(pakfire, path);
+       if (!check) {
+               ERROR(pakfire, "Could not match file %s: %m\n", path);
+               return 1;
+       }
+
+       // Should this file exist at all?
+       if (check->flags & PAKFIRE_FHS_MUSTNOTEXIST) {
+               ERROR(pakfire, "%s must not exist here\n", path);
+               return 1;
+       }
+
+       // Check type & mode
+       r = pakfire_fhs_check_mode(pakfire, check, file);
+       if (r)
+               return r;
+
+       // Check passed!
+       return 0;
+}
diff --git a/src/libpakfire/include/pakfire/fhs.h b/src/libpakfire/include/pakfire/fhs.h
new file mode 100644 (file)
index 0000000..5a28dde
--- /dev/null
@@ -0,0 +1,29 @@
+/*#############################################################################
+#                                                                             #
+# Pakfire - The IPFire package management system                              #
+# Copyright (C) 2021 Pakfire development team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#ifndef PAKFIRE_FHS_H
+#define PAKFIRE_FHS_H
+
+#include <pakfire/file.h>
+#include <pakfire/pakfire.h>
+
+int pakfire_fhs_check_file(struct pakfire* pakfire, struct pakfire_file* file);
+
+#endif /* PAKFIRE_FHS_H */