# ------------------------------------------------------------------------------
dist_scripts_SCRIPTS = \
- src/scripts/check-interpreters \
src/scripts/compress-man-pages \
src/scripts/find-prerequires \
src/scripts/find-provides \
}
static const char* post_build_scripts[] = {
- "check-interpreters",
"compress-man-pages",
"strip",
NULL,
# #
#############################################################################*/
+#include <ctype.h>
#include <errno.h>
#include <fnmatch.h>
#include <libgen.h>
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;
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))
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)
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);
+++ /dev/null
-#!/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 $?