diff options
Diffstat (limited to 'libexec')
| -rw-r--r-- | libexec/nuageinit/nuage.lua | 43 | ||||
| -rwxr-xr-x | libexec/nuageinit/nuageinit | 17 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/Makefile | 4 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/adddoas.lua | 64 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/addsudo.lua | 61 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/decode_base64.lua | 61 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/nuage.sh | 30 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/nuageinit.sh | 6 | ||||
| -rw-r--r-- | libexec/nuageinit/tests/update_sshd_config.lua | 73 | ||||
| -rw-r--r-- | libexec/rc/rc.conf | 6 | ||||
| -rw-r--r-- | libexec/rc/rc.d/virtual_oss | 28 | ||||
| -rw-r--r-- | libexec/rc/safe_eval.sh | 7 | ||||
| -rw-r--r-- | libexec/rc/tests/Makefile | 8 | ||||
| -rw-r--r-- | libexec/rc/tests/safe_eval_test.sh | 65 | ||||
| -rw-r--r-- | libexec/talkd/announce.c | 3 | ||||
| -rw-r--r-- | libexec/tftpd/tftp-io.c | 109 |
16 files changed, 492 insertions, 93 deletions
diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua index 2d962b540b23..f3c23a7c3eb8 100644 --- a/libexec/nuageinit/nuage.lua +++ b/libexec/nuageinit/nuage.lua @@ -52,6 +52,10 @@ local function decode_base64(input) return table.concat(result) end +local function shell_escape(s) + return "'" .. string.gsub(s, "'", "'\\''") .. "'" +end + local function warnmsg(str, prepend) if not str then return @@ -121,7 +125,7 @@ local function sethostname(hostname) warnmsg("Impossible to open " .. hostnamepath .. ":" .. err) return end - f:write('hostname="' .. hostname .. '"\n') + f:write('hostname="' .. hostname:gsub('"', '\\"') .. '"\n') f:close() end @@ -199,7 +203,7 @@ local function adduser(pwd) if root then cmd = cmd .. "-R " .. root .. " " end - local f = io.popen(cmd .. " usershow " .. pwd.name .. " -7 2> /dev/null") + local f = io.popen(cmd .. " usershow " .. shell_escape(pwd.name) .. " -7 2> /dev/null") local pwdstr = f:read("*a") f:close() if pwdstr:len() ~= 0 then @@ -220,13 +224,17 @@ local function adduser(pwd) -- a warning but creates the user anyway. list = purge_group(list) if #list > 0 then - extraargs = " -G " .. table.concat(list, ",") + local escaped_list = {} + for _, g in ipairs(list) do + table.insert(escaped_list, shell_escape(g)) + end + extraargs = " -G " .. table.concat(escaped_list, ",") end end -- pw will automatically create a group named after the username -- do not add a -g option in this case if pwd.primary_group and pwd.primary_group ~= pwd.name then - extraargs = extraargs .. " -g " .. pwd.primary_group + extraargs = extraargs .. " -g " .. shell_escape(pwd.primary_group) end if not pwd.no_create_home then extraargs = extraargs .. " -m " @@ -248,9 +256,9 @@ local function adduser(pwd) if root then cmd = cmd .. "-R " .. root .. " " end - cmd = cmd .. "useradd -n " .. pwd.name .. " -M 0755 -w none " - cmd = cmd .. extraargs .. " -c '" .. pwd.gecos - cmd = cmd .. "' -d '" .. pwd.homedir .. "' -s " .. pwd.shell .. postcmd + cmd = cmd .. "useradd -n " .. shell_escape(pwd.name) .. " -M 0755 -w none " + cmd = cmd .. extraargs .. " -c " .. shell_escape(pwd.gecos) + cmd = cmd .. " -d " .. shell_escape(pwd.homedir) .. " -s " .. shell_escape(pwd.shell) .. postcmd f = io.popen(cmd, "w") if input then @@ -267,7 +275,7 @@ local function adduser(pwd) if root then cmd = cmd .. "-R " .. root .. " " end - cmd = cmd .. "lock " .. pwd.name + cmd = cmd .. "lock " .. shell_escape(pwd.name) os.execute(cmd) end return pwd.homedir @@ -283,7 +291,7 @@ local function addgroup(grp) if root then cmd = cmd .. "-R " .. root .. " " end - local f = io.popen(cmd .. " groupshow " .. grp.name .. " 2> /dev/null") + local f = io.popen(cmd .. " groupshow " .. shell_escape(grp.name) .. " 2> /dev/null") local grpstr = f:read("*a") f:close() if grpstr:len() ~= 0 then @@ -292,13 +300,17 @@ local function addgroup(grp) local extraargs = "" if grp.members then local list = splitlist(grp.members) - extraargs = " -M " .. table.concat(list, ",") + local escaped_list = {} + for _, m in ipairs(list) do + table.insert(escaped_list, shell_escape(m)) + end + extraargs = " -M " .. table.concat(escaped_list, ",") end cmd = "pw " if root then cmd = cmd .. "-R " .. root .. " " end - cmd = cmd .. "groupadd -n " .. grp.name .. extraargs + cmd = cmd .. "groupadd -n " .. shell_escape(grp.name) .. extraargs local r = os.execute(cmd) if not r then warnmsg("fail to add group " .. grp.name) @@ -484,7 +496,7 @@ local function exec_change_password(user, password, type, expire) postcmd = " -w random" end end - cmd = cmd .. "usermod " .. user .. postcmd + cmd = cmd .. "usermod " .. shell_escape(user) .. postcmd if expire then cmd = cmd .. " -p 1" else @@ -577,7 +589,7 @@ local function settimezone(timezone) root = "/" end - local f, _, rc = os.execute("tzsetup -s -C " .. root .. " " .. timezone) + local f, _, rc = os.execute("tzsetup -s -C " .. shell_escape(root) .. " " .. shell_escape(timezone)) if not f then warnmsg("Impossible to configure time zone ( rc = " .. rc .. " )") @@ -600,8 +612,8 @@ local function install_package(package) if package == nil then return true end - local install_cmd = "pkg install -y " .. package - local test_cmd = "pkg info -q " .. package + local install_cmd = "pkg install -y " .. shell_escape(package) + local test_cmd = "pkg info -q " .. shell_escape(package) if os.getenv("NUAGE_RUN_TESTS") then print(install_cmd) print(test_cmd) @@ -683,6 +695,7 @@ local function addfile(file, defer) end local n = { + shell_escape = shell_escape, warn = warnmsg, err = errmsg, chmod = chmod, diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit index a1ebd3f52b25..fc8d9582b9c6 100755 --- a/libexec/nuageinit/nuageinit +++ b/libexec/nuageinit/nuageinit @@ -67,7 +67,14 @@ local function open_resolv_conf() end local function open_resolvconf_conf() - return openat("/etc", "resolvconf.conf") + local path_dir = root .. "/etc" + local path_name = path_dir .. "/resolvconf.conf" + nuage.mkdir_p(path_dir) + local f, err = io.open(path_name, "a") + if not f then + nuage.err("unable to open " .. path_name .. ": " .. err) + end + return f, path_name end local function get_ifaces_by_mac() @@ -271,8 +278,9 @@ local function nameservers(interface, obj) end -- Only call resolvconf with interface if interface is provided + local resolvconf_command if interface then - resolvconf_command = "resolvconf -a " .. interface .. " < " .. resolv_conf + resolvconf_command = "resolvconf -a " .. nuage.shell_escape(interface) .. " < " .. resolv_conf else resolvconf_command = "resolvconf -u" end @@ -738,6 +746,11 @@ local function load_userdata() return line, obj end +-- Clean up stale resolvconf.conf from previous boot +if citype ~= "postnet" then + os.remove(root .. "/etc/resolvconf.conf") +end + if citype == "config-2" then -- network config2_network(ni_path) diff --git a/libexec/nuageinit/tests/Makefile b/libexec/nuageinit/tests/Makefile index dc8997717b59..fc7765268660 100644 --- a/libexec/nuageinit/tests/Makefile +++ b/libexec/nuageinit/tests/Makefile @@ -18,5 +18,9 @@ ${PACKAGE}FILES+= sethostname.lua ${PACKAGE}FILES+= settimezone.lua ${PACKAGE}FILES+= warn.lua ${PACKAGE}FILES+= addfile.lua +${PACKAGE}FILES+= decode_base64.lua +${PACKAGE}FILES+= addsudo.lua +${PACKAGE}FILES+= adddoas.lua +${PACKAGE}FILES+= update_sshd_config.lua .include <bsd.test.mk> diff --git a/libexec/nuageinit/tests/adddoas.lua b/libexec/nuageinit/tests/adddoas.lua new file mode 100644 index 000000000000..d4bab41ecc3d --- /dev/null +++ b/libexec/nuageinit/tests/adddoas.lua @@ -0,0 +1,64 @@ +#!/usr/libexec/flua +--- +-- SPDX-License-Identifier: BSD-2-Clause +-- +-- Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org> + +local n = require("nuage") + +local root = os.getenv("NUAGE_FAKE_ROOTDIR") +if not root then + root = "" +end + +local function get_localbase() + local f = io.popen("sysctl -in user.localbase 2> /dev/null") + local lb = f:read("*l") + f:close() + if lb == nil or lb:len() == 0 then + lb = "/usr/local" + end + return lb +end + +local function read_doasconf() + local path = root .. get_localbase() .. "/etc/doas.conf" + local f = io.open(path, "r") + if not f then + return nil + end + local content = f:read("*a") + f:close() + return content +end + +-- test with a single string rule with %u substitution +n.adddoas({ name = "testuser", doas = "permit persist %u as root" }) +local content = read_doasconf() +if not content then + n.err("doas.conf not created") +end +if content ~= "permit persist testuser as root\n" then + n.err("unexpected doas.conf content with %u: '" .. content .. "'") +end + +-- remove file for next test +os.remove(root .. get_localbase() .. "/etc/doas.conf") + +-- test with a table of rules +n.adddoas({ + name = "testuser", + doas = { + "deny %u as foobar", + "permit persist %u as root cmd whoami" + } +}) +content = read_doasconf() +if not content then + n.err("doas.conf not created for table") +end +if content ~= "deny testuser as foobar\npermit persist testuser as root cmd whoami\n" then + n.err("unexpected doas.conf content for table: '" .. content .. "'") +end + +os.exit(0) diff --git a/libexec/nuageinit/tests/addsudo.lua b/libexec/nuageinit/tests/addsudo.lua new file mode 100644 index 000000000000..7fc5865d83f4 --- /dev/null +++ b/libexec/nuageinit/tests/addsudo.lua @@ -0,0 +1,61 @@ +#!/usr/libexec/flua +--- +-- SPDX-License-Identifier: BSD-2-Clause +-- +-- Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org> + +local n = require("nuage") + +local root = os.getenv("NUAGE_FAKE_ROOTDIR") +if not root then + root = "" +end + +local function get_localbase() + local f = io.popen("sysctl -in user.localbase 2> /dev/null") + local lb = f:read("*l") + f:close() + if lb == nil or lb:len() == 0 then + lb = "/usr/local" + end + return lb +end + +local function read_sudoers() + local path = root .. get_localbase() .. "/etc/sudoers.d/90-nuageinit-users" + local f = io.open(path, "r") + if not f then + return nil + end + local content = f:read("*a") + f:close() + return content +end + +-- test with a single string rule +n.addsudo({ name = "testuser", sudo = "ALL=(ALL) NOPASSWD:ALL" }) +local content = read_sudoers() +if not content then + n.err("sudoers file not created") +end +if content ~= "testuser ALL=(ALL) NOPASSWD:ALL\n" then + n.err("unexpected sudoers content for string rule: '" .. content .. "'") +end + +-- remove file for next test +os.remove(root .. get_localbase() .. "/etc/sudoers.d/90-nuageinit-users") + +-- test with a table of rules +n.addsudo({ + name = "testuser", + sudo = { "ALL=(ALL) NOPASSWD:/usr/sbin/pw", "ALL=(ALL) ALL" } +}) +content = read_sudoers() +if not content then + n.err("sudoers file not created for table") +end +if content ~= "testuser ALL=(ALL) NOPASSWD:/usr/sbin/pw\ntestuser ALL=(ALL) ALL\n" then + n.err("unexpected sudoers content for table: '" .. content .. "'") +end + +os.exit(0) diff --git a/libexec/nuageinit/tests/decode_base64.lua b/libexec/nuageinit/tests/decode_base64.lua new file mode 100644 index 000000000000..0951d77f0ed7 --- /dev/null +++ b/libexec/nuageinit/tests/decode_base64.lua @@ -0,0 +1,61 @@ +#!/usr/libexec/flua +--- +-- SPDX-License-Identifier: BSD-2-Clause +-- +-- Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org> + +local n = require("nuage") + +-- decode_base64 is not exported, test via addfile + +local function test_decode(input, expected) + local r, err = n.addfile({ + content = input, + encoding = "base64", + path = "/tmp/nuage_test_b64" + }, false) + if not r then + n.err(err) + end + local root = os.getenv("NUAGE_FAKE_ROOTDIR") + if not root then + root = "" + end + local f = assert(io.open(root .. "/tmp/nuage_test_b64", "r")) + local str = f:read("*all") + f:close() + if str ~= expected then + n.err("base64 decode failed: expected '" .. expected + .. "' got '" .. str .. "'") + end +end + +-- empty input +test_decode("", "") + +-- single byte: 'a' +test_decode("YQ==", "a") + +-- two bytes: 'ab' +test_decode("YWI=", "ab") + +-- three bytes: 'abc' +test_decode("YWJj", "abc") + +-- newline in base64 +test_decode("YmxhCg==", "bla\n") + +-- spaces should be ignored +test_decode("Y Q = =", "a") + +-- b64 alias +local r, err = n.addfile({ + content = "YQ==", + encoding = "b64", + path = "/tmp/nuage_test_b64_b64" +}, false) +if not r then + n.err("b64 encoding alias should work: " .. tostring(err)) +end + +os.exit(0) diff --git a/libexec/nuageinit/tests/nuage.sh b/libexec/nuageinit/tests/nuage.sh index 57d83b62928a..348a8d93ba09 100644 --- a/libexec/nuageinit/tests/nuage.sh +++ b/libexec/nuageinit/tests/nuage.sh @@ -14,6 +14,10 @@ atf_test_case adduser atf_test_case adduser_passwd atf_test_case addgroup atf_test_case addfile +atf_test_case decode_base64 +atf_test_case addsudo +atf_test_case adddoas +atf_test_case update_sshd_config settimezone_body() { @@ -90,6 +94,28 @@ addfile_body() atf_check /usr/libexec/flua $(atf_get_srcdir)/addfile.lua } +decode_base64_body() +{ + mkdir tmp + atf_check /usr/libexec/flua $(atf_get_srcdir)/decode_base64.lua +} + +addsudo_body() +{ + atf_check /usr/libexec/flua $(atf_get_srcdir)/addsudo.lua +} + +adddoas_body() +{ + atf_check /usr/libexec/flua $(atf_get_srcdir)/adddoas.lua +} + +update_sshd_config_body() +{ + mkdir -p etc/ssh + atf_check /usr/libexec/flua $(atf_get_srcdir)/update_sshd_config.lua +} + atf_init_test_cases() { atf_add_test_case sethostname @@ -98,4 +124,8 @@ atf_init_test_cases() atf_add_test_case adduser_passwd atf_add_test_case addgroup atf_add_test_case addfile + atf_add_test_case decode_base64 + atf_add_test_case addsudo + atf_add_test_case adddoas + atf_add_test_case update_sshd_config } diff --git a/libexec/nuageinit/tests/nuageinit.sh b/libexec/nuageinit/tests/nuageinit.sh index 1fd68d5a178e..89207fdf0aca 100644 --- a/libexec/nuageinit/tests/nuageinit.sh +++ b/libexec/nuageinit/tests/nuageinit.sh @@ -801,7 +801,7 @@ packages: - yeah/plop EOF chmod 755 "${PWD}"/media/nuageinit/user_data - atf_check -s exit:0 -o inline:"pkg install -y yeah/plop\npkg info -q yeah/plop\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet + atf_check -s exit:0 -o inline:"pkg install -y 'yeah/plop'\npkg info -q 'yeah/plop'\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet cat > media/nuageinit/user_data << 'EOF' #cloud-config @@ -809,7 +809,7 @@ packages: - curl EOF chmod 755 "${PWD}"/media/nuageinit/user_data - atf_check -o inline:"pkg install -y curl\npkg info -q curl\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet + atf_check -o inline:"pkg install -y 'curl'\npkg info -q 'curl'\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet cat > media/nuageinit/user_data << 'EOF' #cloud-config @@ -818,7 +818,7 @@ packages: - meh: bla EOF chmod 755 "${PWD}"/media/nuageinit/user_data - atf_check -o inline:"pkg install -y curl\npkg info -q curl\n" -e inline:"nuageinit: Invalid type: table for packages entry number 2\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet + atf_check -o inline:"pkg install -y 'curl'\npkg info -q 'curl'\n" -e inline:"nuageinit: Invalid type: table for packages entry number 2\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet } config2_userdata_update_packages_body() diff --git a/libexec/nuageinit/tests/update_sshd_config.lua b/libexec/nuageinit/tests/update_sshd_config.lua new file mode 100644 index 000000000000..ac56c29986ac --- /dev/null +++ b/libexec/nuageinit/tests/update_sshd_config.lua @@ -0,0 +1,73 @@ +#!/usr/libexec/flua +--- +-- SPDX-License-Identifier: BSD-2-Clause +-- +-- Copyright (c) 2026 Baptiste Daroussin <bapt@FreeBSD.org> + +local n = require("nuage") + +local root = os.getenv("NUAGE_FAKE_ROOTDIR") +if not root then + root = "" +end + +local sshd_config = root .. "/etc/ssh/sshd_config" + +local function setup(content) + local dir = root .. "/etc/ssh" + n.mkdir_p(dir) + local f = assert(io.open(sshd_config, "w")) + f:write(content) + f:close() +end + +local function read_config() + local f = assert(io.open(sshd_config, "r")) + local content = f:read("*a") + f:close() + return content +end + +-- Key not found: appended +setup("SomeOtherKey yes\n") +n.update_sshd_config("PasswordAuthentication", "yes") +if read_config() ~= "SomeOtherKey yes\nPasswordAuthentication yes\n" then + n.err("Key not found: should be appended") +end + +-- Key with same value: no change +setup("PasswordAuthentication yes\n") +n.update_sshd_config("PasswordAuthentication", "yes") +if read_config() ~= "PasswordAuthentication yes\n" then + n.err("Same value: should not change") +end + +-- Key with different value: changed +setup("PasswordAuthentication no\n") +n.update_sshd_config("PasswordAuthentication", "yes") +if read_config() ~= "PasswordAuthentication yes\n" then + n.err("Different value: should change") +end + +-- Key with comment +setup("PasswordAuthentication no # keep this\n") +n.update_sshd_config("PasswordAuthentication", "yes") +if read_config() ~= "PasswordAuthentication yes\n" then + n.err("Comment stripped: '" .. read_config() .. "'") +end + +-- Case insensitive key matching +setup("passwordauthentication no\n") +n.update_sshd_config("PasswordAuthentication", "yes") +if read_config() ~= "PasswordAuthentication yes\n" then + n.err("Case insensitive matching failed") +end + +-- Extra spaces +setup(" PasswordAuthentication no \n") +n.update_sshd_config("PasswordAuthentication", "yes") +if read_config() ~= "PasswordAuthentication yes\n" then + n.err("Extra spaces handling failed: '" .. read_config() .. "'") +end + +os.exit(0) diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf index 75420e42cdeb..27e8c8456b6f 100644 --- a/libexec/rc/rc.conf +++ b/libexec/rc/rc.conf @@ -736,7 +736,11 @@ newsyslog_flags="-CN" # Newsyslog flags to create marked files mixer_enable="YES" # Run the sound mixer. opensm_enable="NO" # Opensm(8) for infiniband devices defaults to off nuageinit_enable="NO" # Run nuageinit at startup -virtual_oss_enable="NO" # Run virtual_oss at startup + +virtual_oss_enable="NO" # Run virtual_oss at startup. +virtual_oss_configs="dsp" # List of configurations. +virtual_oss_default_control_device="vdsp.ctl" # Default configuration's + # control device. # rctl(8) requires kernel options RACCT and RCTL rctl_enable="YES" # Load rctl(8) rules on boot diff --git a/libexec/rc/rc.d/virtual_oss b/libexec/rc/rc.d/virtual_oss index a25abf256f55..9861545b8bfc 100644 --- a/libexec/rc/rc.d/virtual_oss +++ b/libexec/rc/rc.d/virtual_oss @@ -22,13 +22,9 @@ status_cmd="${name}_status" required_modules="cuse" -configs= pidpath="/var/run/${name}" -default_unit=$(sysctl -n hw.snd.default_unit 2> /dev/null) - -# Default configuration's control device. -: "${virtual_oss_default_control_device:="vdsp.ctl"}" +default_unit=$(sysctl -n hw.snd.default_unit 2> /dev/null) virtual_oss_default_args="\ -S \ -C 2 \ @@ -39,14 +35,9 @@ virtual_oss_default_args="\ -i 8 \ -f /dev/dsp${default_unit} \ -d dsp \ + -l dsp.loop \ -t ${virtual_oss_default_control_device}" -# Set to NO by default. Set it to "YES" to enable virtual_oss. -: "${virtual_oss_enable:="NO"}" - -# List of configurations to use. Default is "dsp". -: "${virtual_oss_configs:="dsp"}" - # Default (dsp) virtual_oss config. : "${virtual_oss_dsp:="${virtual_oss_default_args}"}" @@ -85,10 +76,17 @@ stop_instance() if [ -z "${instance_args}" ]; then warn "no such config: ${config}" else - startmsg -n "Stopping virtual_oss config: ${config}: " - kill "$(cat "${pidpath}/${config}.pid")" - rm -f "${pidpath}/${config}.pid" - startmsg "done" + pidfile="${pidpath}/${config}.pid" + if [ ! -f "${pidfile}" ]; then + warn "not running: ${config}" + else + pid="$(cat "${pidfile}")" + startmsg -n "Stopping virtual_oss config: ${config}: " + kill "${pid}" + pwait "${pid}" + rm -f "${pidfile}" + startmsg "done" + fi fi } diff --git a/libexec/rc/safe_eval.sh b/libexec/rc/safe_eval.sh index 3b3241ae821d..eb1698472624 100644 --- a/libexec/rc/safe_eval.sh +++ b/libexec/rc/safe_eval.sh @@ -28,11 +28,16 @@ fi # return a safe variable setting # any non-alphanumeric chars other than those in "xtras" # will be replaced with '_' +# Lines containing `` or $() are too likely to result in syntax errors +# so just delete them. # # "xtras" should be used with caution and cannot include ';' # safe_set() { - ${SED:-sed} 's/^[ ]*//;s/[ ]*#.*//;s/^:.*//;/^[A-Za-z_][A-Za-z0-9_]*=/!d;s;[^A-Za-z0-9_. "'"$1"'$,/=:+-];_;g' + ${SED:-sed} -e 's/^[ ]*//;s/[ ]*#.*//;s/^:.*//' \ + -e '/`/d' -e '/\$(/d' \ + -e '/^[A-Za-z_][A-Za-z0-9_]*=/!d;s;[^A-Za-z0-9_. "'"$1"'$,/=:+-];_;g;' \ + -e '/=.*_.*[ ]/s,=\(.*\),="\1",;s,"",",g' } ## diff --git a/libexec/rc/tests/Makefile b/libexec/rc/tests/Makefile index c44c6db90b77..3a6eafea292d 100644 --- a/libexec/rc/tests/Makefile +++ b/libexec/rc/tests/Makefile @@ -1,3 +1,9 @@ -ATF_TESTS_SH+= rc_subr_test +ATF_TESTS_SH+= rc_subr_test safe_eval_test + +# allow running this as part of the build - in DIRDEPS_BUILD at least +.if ${.MAKE.LEVEL} > 0 && ${MACHINE:Nhost*} == "" +SAFE_EVAL:= ${_PARSEDIR:U${.PARSEDIR:tA}:H}/safe_eval.sh +.export SAFE_EVAL +.endif .include <bsd.test.mk> diff --git a/libexec/rc/tests/safe_eval_test.sh b/libexec/rc/tests/safe_eval_test.sh new file mode 100644 index 000000000000..a0f3a2784098 --- /dev/null +++ b/libexec/rc/tests/safe_eval_test.sh @@ -0,0 +1,65 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright 2026 Simon J Gerraty +# + +atf_test_case safe_set_reject +safe_set_reject_head() +{ + atf_set "descr" "Verify that safe_set rejects shell meta chars" +} + +safe_set_reject_body() +{ + __name="$(atf_get ident)" + __input=$(mktemp -t "${__name}.input") + + cat <<'EOF' > "$__input" +: ignore=this +# ignore this too +# avoid # in the middle of a quoted value like: +# oops="this # will cause synatx error" +quoted="this and that" +simple=ok # trailing comments ignored + also=ok # leading white-space ignored + also_wik=ok +host=`hostname`' # backtics - delete line +os=$(uname -s) # $() - delete line +oops=one;hostname' # replace ; with _ so: one_hostname +regex="prefix[abc-]*" # []* replaced with _ +EOF + + __output=$(safe_set < "$__input" | tr '"\012' '\047;') + atf_check_equal "$__output" "quoted='this and that';simple=ok;also=ok;also_wik=ok;oops=one_hostname_;regex='prefix_abc-__';" +} + + +atf_test_case safe_set_xtras +safe_set_xtras_head() +{ + atf_set "descr" "Verify that safe_set handles extra allowed chars" +} + +safe_set_xtras_body() +{ + __name="$(atf_get ident)" + __input=$(mktemp -t "${__name}.input") + + cat <<'EOF' > "$__input" +: ignore=this +# ignore this too +regex="prefix[abc-]*" +EOF + + __output=$(safe_set "[]*" < "$__input" | tr '"\012' '\047;') + atf_check_equal "$__output" "regex='prefix[abc-]*';" +} + +atf_init_test_cases() +{ + SAFE_EVAL=${SAFE_EVAL:-/libexec/safe_eval.sh} + . $SAFE_EVAL + atf_add_test_case safe_set_reject + atf_add_test_case safe_set_xtras +} diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c index 80c663ba48b4..cfe8b4eae65e 100644 --- a/libexec/talkd/announce.c +++ b/libexec/talkd/announce.c @@ -40,6 +40,7 @@ #include <errno.h> #include <paths.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -152,7 +153,7 @@ print_mesg(const char *tty, CTL_MSG *request, * stack up processes trying to write messages to a tty * that is permanently blocked. */ - if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL) + if (ttymsg(&iovec, 1, tty, RING_WAIT - 5, true) != 0) return (FAILED); return (SUCCESS); diff --git a/libexec/tftpd/tftp-io.c b/libexec/tftpd/tftp-io.c index 50102e652d2f..3697c13f358f 100644 --- a/libexec/tftpd/tftp-io.c +++ b/libexec/tftpd/tftp-io.c @@ -69,17 +69,12 @@ static struct errmsg { { -1, NULL } }; -#define DROPPACKET(s) \ +#define DROPPACKET(...) \ if (packetdroppercentage != 0 && \ - arc4random()%100 < packetdroppercentage) { \ - tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ - return; \ - } -#define DROPPACKETn(s,n) \ - if (packetdroppercentage != 0 && \ - arc4random()%100 < packetdroppercentage) { \ - tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ - return (n); \ + arc4random() % 100 < packetdroppercentage) { \ + tftp_log(LOG_DEBUG, "Artificial packet drop in %s", \ + __func__); \ + return __VA_ARGS__; \ } const char * @@ -104,7 +99,7 @@ send_packet(int peer, uint16_t block, char *pkt, int size) int t = 1; for (i = 0; i < 12 ; i++) { - DROPPACKETn("send_packet", 0); + DROPPACKET(0); if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) == size) { @@ -144,7 +139,7 @@ send_error(int peer, int error) if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d", error); - DROPPACKET("send_error"); + DROPPACKET(); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); @@ -173,35 +168,35 @@ send_error(int peer, int error) int send_wrq(int peer, char *filename, char *mode) { - int n; + char buf[MAXPKTSIZE]; struct tftphdr *tp; char *bp; - char buf[MAXPKTSIZE]; - int size; + size_t len; + int n, size; - if (debug & DEBUG_PACKETS) + if (debug & DEBUG_PACKETS) { tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", - filename, mode - ); + filename, mode); + } - DROPPACKETn("send_wrq", 0); + DROPPACKET(0); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)WRQ); size = offsetof(struct tftphdr, th_stuff); bp = tp->th_stuff; - strlcpy(bp, filename, sizeof(buf) - size); - bp += strlen(filename); - *bp = 0; - bp++; - size += strlen(filename) + 1; + len = strlcpy(bp, filename, sizeof(buf) - size); + if (len >= sizeof(buf) - size) + goto overflow; + bp += len + 1; + size += len + 1; - strlcpy(bp, mode, sizeof(buf) - size); - bp += strlen(mode); - *bp = 0; - bp++; - size += strlen(mode) + 1; + len = strlcpy(bp, mode, sizeof(buf) - size); + if (len >= sizeof(buf) - size) + goto overflow; + bp += len + 1; + size += len + 1; if (options_rfc_enabled) size += make_options(peer, bp, sizeof(buf) - size); @@ -209,10 +204,13 @@ send_wrq(int peer, char *filename, char *mode) n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { - tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); + tftp_log(LOG_ERR, "%s: %m", __func__); return (1); } return (0); +overflow: + tftp_log(LOG_ERR, "%s: file name too long", __func__); + return (1); } /* @@ -221,35 +219,35 @@ send_wrq(int peer, char *filename, char *mode) int send_rrq(int peer, char *filename, char *mode) { - int n; + char buf[MAXPKTSIZE]; struct tftphdr *tp; char *bp; - char buf[MAXPKTSIZE]; - int size; + size_t len; + int n, size; - if (debug & DEBUG_PACKETS) + if (debug & DEBUG_PACKETS) { tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", - filename, mode - ); + filename, mode); + } - DROPPACKETn("send_rrq", 0); + DROPPACKET(0); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)RRQ); size = offsetof(struct tftphdr, th_stuff); bp = tp->th_stuff; - strlcpy(bp, filename, sizeof(buf) - size); - bp += strlen(filename); - *bp = 0; - bp++; - size += strlen(filename) + 1; + len = strlcpy(bp, filename, sizeof(buf) - size); + if (len >= sizeof(buf) - size) + goto overflow; + bp += len + 1; + size += len + 1; - strlcpy(bp, mode, sizeof(buf) - size); - bp += strlen(mode); - *bp = 0; - bp++; - size += strlen(mode) + 1; + len = strlcpy(bp, mode, sizeof(buf) - size); + if (len >= sizeof(buf) - size) + goto overflow; + bp += len + 1; + size += len + 1; if (options_rfc_enabled) { options_set_request(OPT_TSIZE, "0"); @@ -259,10 +257,13 @@ send_rrq(int peer, char *filename, char *mode) n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { - tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); + tftp_log(LOG_ERR, "%s: %m", __func__); return (1); } return (0); +overflow: + tftp_log(LOG_ERR, "%s: file name too long", __func__); + return (1); } /* @@ -279,7 +280,7 @@ send_oack(int peer) if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending OACK"); - DROPPACKETn("send_oack", 0); + DROPPACKET(0); /* * Send back an options acknowledgement (only the ones with @@ -293,8 +294,8 @@ send_oack(int peer) if (options[i].o_reply != NULL) { n = snprintf(bp, size, "%s%c%s", options[i].o_type, 0, options[i].o_reply); - bp += n+1; - size -= n+1; + bp += n + 1; + size -= n + 1; if (size < 0) { tftp_log(LOG_ERR, "oack: buffer overflow"); exit(1); @@ -325,7 +326,7 @@ send_ack(int fp, uint16_t block) if (debug & DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); - DROPPACKETn("send_ack", 0); + DROPPACKET(0); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ACK); @@ -355,7 +356,7 @@ send_data(int peer, uint16_t block, char *data, int size) tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", block, size); - DROPPACKETn("send_data", 0); + DROPPACKET(0); pkt = (struct tftphdr *)buf; @@ -402,7 +403,7 @@ receive_packet(int peer, char *data, int size, struct sockaddr_storage *from, fromlen = sizeof(*pfrom); n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); - DROPPACKETn("receive_packet", RP_TIMEOUT); + DROPPACKET(RP_TIMEOUT); if (n < 0) { /* No idea what could have happened if it isn't a timeout */ |
