From: Michael Tremer Date: Fri, 14 Apr 2023 10:29:18 +0000 (+0000) Subject: file: Check for invalid script interpreters X-Git-Tag: 0.9.29~193 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=375a06e37b9881527646d967dd470e9b7fca95c3;p=pakfire.git file: Check for invalid script interpreters Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 010f7f4f9..e70b08ce5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/src/libpakfire/build.c b/src/libpakfire/build.c index 13c4c87f4..40464569a 100644 --- a/src/libpakfire/build.c +++ b/src/libpakfire/build.c @@ -1294,7 +1294,6 @@ ERROR: } static const char* post_build_scripts[] = { - "check-interpreters", "compress-man-pages", "strip", NULL, diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index 07a5da966..663c2e38f 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -18,6 +18,7 @@ # # #############################################################################*/ +#include #include #include #include @@ -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) diff --git a/src/libpakfire/include/pakfire/file.h b/src/libpakfire/include/pakfire/file.h index 8030fae78..a09622078 100644 --- a/src/libpakfire/include/pakfire/file.h +++ b/src/libpakfire/include/pakfire/file.h @@ -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 index ba7e882e6..000000000 --- a/src/scripts/check-interpreters +++ /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 . # -# # -############################################################################### - -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 $?