]> git.ipfire.org Git - people/ms/pakfire.git/commitdiff
file: Check for invalid script interpreters
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Apr 2023 10:29:18 +0000 (10:29 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 26 Apr 2023 15:14:29 +0000 (15:14 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/libpakfire/build.c
src/libpakfire/file.c
src/libpakfire/include/pakfire/file.h
src/scripts/check-interpreters [deleted file]

index 010f7f4f96a39ee09e4cb41e2374d93b907b61f6..e70b08ce5b237c28b987fcc220b5ad740cda2ec8 100644 (file)
@@ -722,7 +722,6 @@ tests_parser_test_LDADD = \
 # ------------------------------------------------------------------------------
 
 dist_scripts_SCRIPTS = \
-       src/scripts/check-interpreters \
        src/scripts/compress-man-pages \
        src/scripts/find-prerequires \
        src/scripts/find-provides \
index 13c4c87f41bfb06c392a7073bffd06c30d3ce94d..40464569ab3d4161d200e795ca0e3177cda5ca2d 100644 (file)
@@ -1294,7 +1294,6 @@ ERROR:
 }
 
 static const char* post_build_scripts[] = {
-       "check-interpreters",
        "compress-man-pages",
        "strip",
        NULL,
index 07a5da966df3765a60095309f341615ec81c0fe1..663c2e38ff57ad522d85bd706e0efd3aaefc00e8 100644 (file)
@@ -18,6 +18,7 @@
 #                                                                             #
 #############################################################################*/
 
+#include <ctype.h>
 #include <errno.h>
 #include <fnmatch.h>
 #include <libgen.h>
@@ -874,6 +875,13 @@ char* pakfire_file_dump(struct pakfire_file* file, int flags) {
                        if (r < 0)
                                goto ERROR;
                }
+
+               // Invalid interpreters
+               if (file->issues & PAKFIRE_FILE_INVALID_INTERPRETER) {
+                       r = asprintf(&buffer, "%s [INVALID-INTERPRETER]", buffer);
+                       if (r < 0)
+                               goto ERROR;
+               }
        }
 
        return buffer;
@@ -2415,6 +2423,136 @@ static int pakfire_file_check_runpath(struct pakfire_file* file) {
        return pakfire_file_open_elf(file, __pakfire_file_check_runpath, NULL);
 }
 
+static int pakfire_file_get_script_interpreter(struct pakfire_file* file, char** interpreter) {
+       FILE* f = NULL;
+       char shebang[1024];
+       char* interp = NULL;
+       char* p = NULL;
+       int r;
+
+       // Check inputs
+       if (!interpreter) {
+               errno = EINVAL;
+               return 1;
+       }
+
+       // Only run for regular files
+       if (!S_ISREG(file->st.st_mode))
+               return 0;
+
+       // Only run for executable files
+       if (!pakfire_file_is_executable(file))
+               return 0;
+
+       // Nothing to do if the file is empty
+       if (!file->st.st_size)
+               return 0;
+
+       // Open the file
+       f = pakfire_file_open(file);
+       if (!f) {
+               r = 1;
+               goto ERROR;
+       }
+
+       // Fill the buffer with the first couple of bytes of the file
+       ssize_t bytes_read = fread(shebang, 1, sizeof(shebang), f);
+
+       // Handle any reading errors
+       if (bytes_read < 0) {
+               ERROR(file->pakfire, "Could not read from file %s: %m\n", file->path);
+               r = 1;
+               goto ERROR;
+       }
+
+       // We did not read enough data
+       if (bytes_read < 2) {
+               r = 1;
+               goto ERROR;
+       }
+
+       if (strncmp("#!", shebang, 2) == 0) {
+               DEBUG(file->pakfire, "%s is a script\n", file->path);
+
+               // Find the end of the first line (to be able to perform string operations)
+               p = memchr(shebang, '\n', sizeof(shebang));
+               if (!p) {
+                       ERROR(file->pakfire, "%s: First line seems to be too long\n", file->path);
+                       errno = ENOBUFS;
+                       r = 1;
+                       goto ERROR;
+               }
+
+               // Terminate the string
+               *p = '\0';
+
+               // Find the beginning of the interpreter
+               interp = shebang + 2;
+
+               // Consume any space between #! and the path
+               while (*interp && isspace(*interp))
+                       interp++;
+
+               p = interp;
+
+               // Find the end of the command (cuts off any options)
+               while (*p) {
+                       if (isspace(*p)) {
+                               *p = '\0';
+                               break;
+                       }
+
+                       p++;
+               }
+
+               // Copy the interpreter to the heap
+               *interpreter = strdup(interp);
+       }
+
+       // Success
+       r = 0;
+
+ERROR:
+       if (f)
+               fclose(f);
+
+       return r;
+}
+
+static int pakfire_file_check_interpreter(struct pakfire_file* file) {
+       char* interpreter = NULL;
+       int r;
+
+       // Fetch the script interpreter
+       r = pakfire_file_get_script_interpreter(file, &interpreter);
+       if (r)
+               return r;
+
+       // If there is no result, the file is not a script
+       if (!interpreter)
+               return 0;
+
+       DEBUG(file->pakfire, "%s: Interpreter: %s\n", file->path, interpreter);
+
+       // Paths must be absolute
+       if (*interpreter != '/')
+               file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
+
+       // Check if the interpreter is in /usr/local
+       else if (pakfire_string_startswith(interpreter, "/usr/local/"))
+               file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
+
+       // We don't support "env"
+       else if (strcmp(interpreter, "/usr/bin/env") == 0)
+               file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
+
+       // Cleanup
+       if (interpreter)
+               free(interpreter);
+
+       return 0;
+}
+
 static int pakfire_file_check_capabilities(struct pakfire_file* file) {
        // Files cannot have capabilities but not be executable
        if (!pakfire_file_is_executable(file) && pakfire_file_has_caps(file))
@@ -2589,6 +2727,11 @@ int pakfire_file_check(struct pakfire_file* file, int* issues) {
                if (r)
                        file->issues |= PAKFIRE_FILE_FHS_ERROR;
 
+               // Perform interpreter check
+               r = pakfire_file_check_interpreter(file);
+               if (r)
+                       return r;
+
                // Perform capability check
                r = pakfire_file_check_capabilities(file);
                if (r)
index 8030fae7811a15be804303e778f709f6a5caa7a1..a09622078d35d49930e5dcac0dbe9ee10856f36f 100644 (file)
@@ -186,14 +186,15 @@ int pakfire_file_verify(struct pakfire_file* file, int* status);
        Checks
 */
 enum pakfire_file_check_issues {
-       PAKFIRE_FILE_FHS_ERROR          = (1 << 0),
-       PAKFIRE_FILE_MISSING_DEBUGINFO  = (1 << 1),
-       PAKFIRE_FILE_MISSING_SSP        = (1 << 2),
-       PAKFIRE_FILE_MISSING_PIE        = (1 << 3),
-       PAKFIRE_FILE_EXECSTACK          = (1 << 4),
-       PAKFIRE_FILE_NO_RELRO           = (1 << 5),
-       PAKFIRE_FILE_HAS_RUNPATH        = (1 << 6),
-       PAKFIRE_FILE_INVALID_CAPS       = (1 << 7),
+       PAKFIRE_FILE_FHS_ERROR           = (1 << 0),
+       PAKFIRE_FILE_MISSING_DEBUGINFO   = (1 << 1),
+       PAKFIRE_FILE_MISSING_SSP         = (1 << 2),
+       PAKFIRE_FILE_MISSING_PIE         = (1 << 3),
+       PAKFIRE_FILE_EXECSTACK           = (1 << 4),
+       PAKFIRE_FILE_NO_RELRO            = (1 << 5),
+       PAKFIRE_FILE_HAS_RUNPATH         = (1 << 6),
+       PAKFIRE_FILE_INVALID_CAPS        = (1 << 7),
+       PAKFIRE_FILE_INVALID_INTERPRETER = (1 << 8),
 };
 
 int pakfire_file_check(struct pakfire_file* file, int* issues);
diff --git a/src/scripts/check-interpreters b/src/scripts/check-interpreters
deleted file mode 100644 (file)
index ba7e882..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/bash
-###############################################################################
-#                                                                             #
-# 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/>.       #
-#                                                                             #
-###############################################################################
-
-error() {
-       echo "$@" >&2
-}
-
-main() {
-       local buildroot="${1}"
-       shift
-
-       # Check if BUILDROOT exists
-       if [ ! -d "${buildroot}" ]; then
-               error "BUILDROOT does not exist"
-               return 1
-       fi
-
-       local file
-       local -A files
-
-       # Find all executable files
-       for file in $(find "${buildroot}" -type f -perm /111 | sort); do
-               local shebang="$(head -c2 "${file}")"
-
-               # Skip files that are not scripts
-               if [ "${shebang:0:2}" != "#!" ]; then
-                       continue
-               fi
-
-               local interpreter="$(head -n1 "${file}")"
-               interpreter="${interpreter:2}"
-
-               case "${interpreter}" in
-                       # Interpreters in /usr/local are illegal
-                       /usr/local/*)
-                               files["${file}"]="${interpreter}"
-                               continue
-                               ;;
-
-                       # We don't support "env"
-                       /usr/bin/env*)
-                               # Automatically fix this
-                               sed -i "${file}" \
-                                       -e "s,#!/usr/bin/env \(/usr/bin/.*\),\1,"
-                               ;;
-               esac
-       done
-
-       if [ "${#files[@]}" -gt 0 ]; then
-               error "Illegal interpreters found:"
-               local file
-               for file in "${!files[@]}"; do
-                       error "  ${file/${buildroot}/} (${files[${file}]})"
-               done
-
-               return 1
-       fi
-
-       return 0
-}
-
-main "$@" || exit $?