diff options
author | Karl Berry <karl@freefriends.org> | 2024-06-30 22:17:00 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2024-06-30 22:17:00 +0000 |
commit | ee038309774ac7c79b5a8a4013eab738cf761c0c (patch) | |
tree | f451b941b48e580ccc50b6a22f8ea7499acc8113 | |
parent | cf99e73a6f064cbe900226e778bf49b2ad4d9765 (diff) |
extractbb.lua wrapper for safety
git-svn-id: svn://tug.org/texlive/trunk@71664 c570f23f-e606-0410-a88d-b1316a301751
21 files changed, 271 insertions, 16 deletions
diff --git a/Build/source/texk/kpathsea/ChangeLog b/Build/source/texk/kpathsea/ChangeLog index b7e89628ec3..cbcee62f617 100644 --- a/Build/source/texk/kpathsea/ChangeLog +++ b/Build/source/texk/kpathsea/ChangeLog @@ -1,3 +1,8 @@ +2024-07-01 Karl Berry <karl@tug.org> + + * texmf.cnf (shell_escape_commands): restore extractbb, + now as a wrapper script that protects against the exploit. + 2024-06-29 Karl Berry <karl@tug.org> * texmf.cnf (shell_escape_commands): remove extractbb. diff --git a/Build/source/texk/kpathsea/texmf.cnf b/Build/source/texk/kpathsea/texmf.cnf index 333e6325b3d..5df6bd97f9c 100644 --- a/Build/source/texk/kpathsea/texmf.cnf +++ b/Build/source/texk/kpathsea/texmf.cnf @@ -649,6 +649,7 @@ shell_escape = p % shell_escape_commands = \ bibtex,bibtex8,\ +extractbb,\ gregorio,\ kpsewhich,\ l3sys-query,\ @@ -660,7 +661,6 @@ r-mpost,\ texosquery-jre8,\ % other programs considered: -% extractbb - allowed in the past, but can be exploited. % dvips - but external commands can be executed, need at least -R1. % epspdf, ps2pdf, pstopdf - need to respect openout_any, % and gs -dSAFER must be used and check for shell injection with filenames. diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.am b/Build/source/texk/texlive/linked_scripts/Makefile.am index ba4e7be48fd..0bd39fbab96 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.am +++ b/Build/source/texk/texlive/linked_scripts/Makefile.am @@ -235,6 +235,7 @@ texmf_other_scripts = \ texfindpkg/texfindpkg.lua \ texdoc/texdoc.tlu \ texfot/texfot.pl \ + texlive/extractbb.lua \ texlive/fmtutil-sys.sh \ texlive/fmtutil-user.sh \ texlive/fmtutil.pl \ diff --git a/Build/source/texk/texlive/linked_scripts/Makefile.in b/Build/source/texk/texlive/linked_scripts/Makefile.in index 447f6518ae1..13b8d4046ad 100644 --- a/Build/source/texk/texlive/linked_scripts/Makefile.in +++ b/Build/source/texk/texlive/linked_scripts/Makefile.in @@ -452,6 +452,7 @@ texmf_other_scripts = \ texfindpkg/texfindpkg.lua \ texdoc/texdoc.tlu \ texfot/texfot.pl \ + texlive/extractbb.lua \ texlive/fmtutil-sys.sh \ texlive/fmtutil-user.sh \ texlive/fmtutil.pl \ diff --git a/Build/source/texk/texlive/linked_scripts/scripts.lst b/Build/source/texk/texlive/linked_scripts/scripts.lst index 479473cdf8c..04eacde6e68 100644 --- a/Build/source/texk/texlive/linked_scripts/scripts.lst +++ b/Build/source/texk/texlive/linked_scripts/scripts.lst @@ -176,6 +176,7 @@ texdirflatten/texdirflatten texfindpkg/texfindpkg.lua texdoc/texdoc.tlu texfot/texfot.pl +texlive/extractbb.lua texlive/fmtutil-sys.sh texlive/fmtutil-user.sh texlive/fmtutil.pl diff --git a/Master/bin/aarch64-linux/extractbb b/Master/bin/aarch64-linux/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/aarch64-linux/extractbb +++ b/Master/bin/aarch64-linux/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/amd64-freebsd/extractbb b/Master/bin/amd64-freebsd/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/amd64-freebsd/extractbb +++ b/Master/bin/amd64-freebsd/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/amd64-netbsd/extractbb b/Master/bin/amd64-netbsd/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/amd64-netbsd/extractbb +++ b/Master/bin/amd64-netbsd/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/armhf-linux/extractbb b/Master/bin/armhf-linux/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/armhf-linux/extractbb +++ b/Master/bin/armhf-linux/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/i386-freebsd/extractbb b/Master/bin/i386-freebsd/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/i386-freebsd/extractbb +++ b/Master/bin/i386-freebsd/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/i386-linux/extractbb b/Master/bin/i386-linux/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/i386-linux/extractbb +++ b/Master/bin/i386-linux/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/i386-netbsd/extractbb b/Master/bin/i386-netbsd/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/i386-netbsd/extractbb +++ b/Master/bin/i386-netbsd/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/i386-solaris/extractbb b/Master/bin/i386-solaris/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/i386-solaris/extractbb +++ b/Master/bin/i386-solaris/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/universal-darwin/extractbb b/Master/bin/universal-darwin/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/universal-darwin/extractbb +++ b/Master/bin/universal-darwin/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/x86_64-cygwin/extractbb b/Master/bin/x86_64-cygwin/extractbb index b011eb0c845..b592ca9b806 120000 --- a/Master/bin/x86_64-cygwin/extractbb +++ b/Master/bin/x86_64-cygwin/extractbb @@ -1 +1 @@ -xdvipdfmx.exe
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/x86_64-darwinlegacy/extractbb b/Master/bin/x86_64-darwinlegacy/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/x86_64-darwinlegacy/extractbb +++ b/Master/bin/x86_64-darwinlegacy/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/x86_64-linux/extractbb b/Master/bin/x86_64-linux/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/x86_64-linux/extractbb +++ b/Master/bin/x86_64-linux/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/x86_64-linuxmusl/extractbb b/Master/bin/x86_64-linuxmusl/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/x86_64-linuxmusl/extractbb +++ b/Master/bin/x86_64-linuxmusl/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/bin/x86_64-solaris/extractbb b/Master/bin/x86_64-solaris/extractbb index 619e6a2f6ab..b592ca9b806 120000 --- a/Master/bin/x86_64-solaris/extractbb +++ b/Master/bin/x86_64-solaris/extractbb @@ -1 +1 @@ -xdvipdfmx
\ No newline at end of file +../../texmf-dist/scripts/texlive/extractbb.lua
\ No newline at end of file diff --git a/Master/texmf-dist/scripts/texlive/extractbb.lua b/Master/texmf-dist/scripts/texlive/extractbb.lua new file mode 100755 index 00000000000..7584814ee80 --- /dev/null +++ b/Master/texmf-dist/scripts/texlive/extractbb.lua @@ -0,0 +1,247 @@ +#!/usr/bin/env texlua +-- $Id$ +-- SPDX-License-Identifier: CC0-1.0 +-- SPDX-FileCopyrightText: 2024 Max Chernoff +-- +-- A generic wrapper to make commands safe to run with restricted shell escape. +-- +-- Originally created for extractbb, which is listed in shell_escape_commands, +-- but can be run as dvipdfm(x), which in turn can run arbitrary commands +-- using its -D option. +-- +-- The idea is to exec "ebb --ebb <other args>", since only argv[1] is +-- used by dvipdfmx to determine its behavior. +-- +-- Note: This script can only adjust the paths and arguments of the target +-- executable; it *CANNOT* make an arbitrary program safe to run with +-- restricted shell escape. + +-- A shorter, less paranoid version. +-- (Prepend a hyphen to the line below to enable). +--[=[ +arg[0] = arg[0]:gsub("extractbb", "ebb") +table.insert(arg, 1, "ebb") +table.insert(arg, 2, "--ebb") +os.exec(arg) +os.exit(1) +--]=] + +--------------------- +--- Configuration --- +--------------------- + +-- The base name of this script. (Example: ``extractbb'') +local SCRIPT_NAME = "extractbb" + +-- The extension of the script. Extensionless-names are also permitted. +-- (Example: ``lua'') +local SCRIPT_EXT = "lua" + +-- The base name of the path to the target program. (Example: ``xdvipdfmx'') +local TARGET_PATH_NAME = "xdvipdfmx" + +-- The name to use when calling the target program. Equivalent to ``argv[0]'' +-- in C. (Example: ``extractbb'') +local TARGET_EXEC_NAME = "ebb" + +-- Any extra arguments to be prepended to the target program, before any +-- user-supplied arguments. Equivalent to ``argv[1], ...'' in C. +-- (Example: ``--extractbb'') +local TARGET_PREPEND_ARGS = { "--ebb" } + +-- Any extra arguments to be appended to the target program, after any +-- user-supplied arguments. Equivalent to ``..., argv[argc]'' in C. +local TARGET_APPEND_ARGS = {} + +-- Sets the value of ``openin_any'' to this value. If ``nil'', then the value +-- will be left unchanged. (Example: ``r'') +local READ_PERMS = "r" + +-- Sets the value of ``openout_any'' to this value. If ``nil'', then the value +-- will be left unchanged. (Example: ``p'') +local WRITE_PERMS = "p" + +-- The name of the Lua interpreter. (Example: ``texlua'') +local INTERPRETER_NAME = "texlua" + +-- The extension of the interpreter. Extensionless-names are also permitted. +-- (Example: ``exe'') +local INTERPRETER_EXT = "exe" + + +---------------------- +--- Initialization --- +---------------------- + +-- Save often-used globals for a slight speed boost. +local insert = table.insert + +-- Set the kpathsea program name +kpse.set_program_name(INTERPRETER_NAME, SCRIPT_NAME) + +-- Rename the input arguments so we don't get confused +local script_args = arg + + +---------------------------- +--- Function Definitions --- +---------------------------- + +-- Error messages +local function error(message) + io.stderr:write("! ERROR (extractbb.lua): " .. message, "\n") + io.stderr:write(debug.traceback(nil, 2), "\n") + io.stderr:flush() + os.exit(1) +end + +-- Get the directory, name, and extension from a full path. We'll split on +-- either a forward or backward slash---Windows can use either, and we don't +-- need to support Unix systems with TL installed to a directory with +-- backslashes in its name. +local split_dir_pattern = "^(.*)[/\\]([^/\\]-)$" +local split_ext_pattern = "(.*)%.([^.]-)$" + +local function split_path(path) + -- Make sure that we were given a string + if type(path) ~= "string" then + return nil, nil, nil + end + + -- Split the (directory) from the (name and extension) + local dir, name_ext = path:match(split_dir_pattern) + + -- No directory + if not dir then + dir = nil + name_ext = path + + -- A bare directory (with a trailing slash) + elseif name_ext == "" then + return dir, nil, nil + end + + -- Split the (name) from the (extension) + local name, ext = name_ext:match(split_ext_pattern) + + -- No extension (or a dotfile) + if (not name) or (name == "") then + name = name_ext + ext = nil + end + + return dir, name, ext +end + +-- See if a file exists +local function file_exists(path) + local mode = lfs.attributes(path, "mode") + return (mode == "file") or (mode == "link") +end + + +--------------------- +--- Safety Checks --- +--------------------- + +-- Make sure that we're running unrestricted. +if status.shell_escape ~= 1 then + error("Shell escape has been disabled.") +end + +if status.safer_option ~= 0 then + error("The ``safer'' option has been enabled.") +end + +-- Set the file permissions. +if READ_PERMS then + os.setenv("openin_any", READ_PERMS) +end + +if WRITE_PERMS then + os.setenv("openout_any", WRITE_PERMS) +end + +-- Get the directory of the script and interpreter +local script_path = debug.getinfo(1, "S").source:sub(2) +local script_dir, script_name, script_ext = split_path(script_path) + +local interpreter_dir = kpse.var_value("SELFAUTOLOC") +local _, interpreter_name, interpreter_ext = split_path(script_args[-1]) +if os.type == 'windows' then + interpreter_ext = INTERPRETER_EXT +end +-- Look up the script again with kpathsea +local resolved_script_path = kpse.find_file( + script_name .. "." .. (script_ext or SCRIPT_EXT), "texmfscripts", true +) + +-- Make sure that our paths are correct +if not script_dir then + error("Empty script dir") +end + +if not resolved_script_path then + error("Empty resolved script path") +end + +if (script_dir ~= interpreter_dir) and (script_path ~= resolved_script_path) then + error("The script is in an incorrect location: " .. script_dir) +end + +if script_name ~= SCRIPT_NAME then + error("Incorrect script name: " .. script_name) +end + +if interpreter_name ~= INTERPRETER_NAME then + error("Incorrect interpreter name: " .. interpreter_name) +end + +if (script_ext ~= SCRIPT_EXT) and (script_ext ~= nil) then + error("Incorrect script extension: " .. script_ext) +end + +if (interpreter_ext ~= INTERPRETER_EXT) and (interpreter_ext ~= nil) then + error("Incorrect interpreter extension: " .. interpreter_ext) +end + +-- Get the path to the target program +local target_ext = interpreter_ext and ("." .. interpreter_ext) or "" +local target_path = interpreter_dir .. "/" .. TARGET_PATH_NAME .. target_ext + +-- Make sure that the target program exists +if not file_exists(target_path) then + error("The target program does not exist: " .. target_path) +end + + +---------------------- +--- Run the target --- +---------------------- + +-- Generate the target arguments +local target_args = { + [0] = target_path, -- Path to the executable + [1] = TARGET_EXEC_NAME, -- argv[0] +} + +-- argv[2] through argv[n] +for _, arg in ipairs(TARGET_PREPEND_ARGS) do + insert(target_args, arg) +end + +for i = 1, #script_args do + -- We use a numeric iterator here to avoid ``arg[-1]'' and ``arg[0]''. + insert(target_args, script_args[i]) +end + +for _, arg in ipairs(TARGET_APPEND_ARGS) do + insert(target_args, arg) +end + +-- Run the target program, replacing the current process +local _, err = os.exec(target_args) + +if err then + error("The target program failed to run.") +end diff --git a/Master/texmf-dist/web2c/texmf.cnf b/Master/texmf-dist/web2c/texmf.cnf index 333e6325b3d..5df6bd97f9c 100644 --- a/Master/texmf-dist/web2c/texmf.cnf +++ b/Master/texmf-dist/web2c/texmf.cnf @@ -649,6 +649,7 @@ shell_escape = p % shell_escape_commands = \ bibtex,bibtex8,\ +extractbb,\ gregorio,\ kpsewhich,\ l3sys-query,\ @@ -660,7 +661,6 @@ r-mpost,\ texosquery-jre8,\ % other programs considered: -% extractbb - allowed in the past, but can be exploited. % dvips - but external commands can be executed, need at least -R1. % epspdf, ps2pdf, pstopdf - need to respect openout_any, % and gs -dSAFER must be used and check for shell injection with filenames. |