aboutsummaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
Diffstat (limited to 'libexec')
-rw-r--r--libexec/nuageinit/nuage.lua43
-rwxr-xr-xlibexec/nuageinit/nuageinit17
-rw-r--r--libexec/nuageinit/tests/Makefile4
-rw-r--r--libexec/nuageinit/tests/adddoas.lua64
-rw-r--r--libexec/nuageinit/tests/addsudo.lua61
-rw-r--r--libexec/nuageinit/tests/decode_base64.lua61
-rw-r--r--libexec/nuageinit/tests/nuage.sh30
-rw-r--r--libexec/nuageinit/tests/nuageinit.sh6
-rw-r--r--libexec/nuageinit/tests/update_sshd_config.lua73
-rw-r--r--libexec/rc/rc.conf6
-rw-r--r--libexec/rc/rc.d/virtual_oss28
-rw-r--r--libexec/rc/safe_eval.sh7
-rw-r--r--libexec/rc/tests/Makefile8
-rw-r--r--libexec/rc/tests/safe_eval_test.sh65
-rw-r--r--libexec/talkd/announce.c3
-rw-r--r--libexec/tftpd/tftp-io.c109
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 */