The branch main has been updated by bapt:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=0211c8722ff2ac9367565e526e62837745bb2cce

commit 0211c8722ff2ac9367565e526e62837745bb2cce
Author:     Baptiste Daroussin <[email protected]>
AuthorDate: 2026-06-09 14:16:44 +0000
Commit:     Baptiste Daroussin <[email protected]>
CommitDate: 2026-06-09 16:04:24 +0000

    nuageinit: fix shell command injection in multiple rc.conf.d writes
---
 libexec/nuageinit/nuageinit          | 89 ++++++++++++------------------------
 libexec/nuageinit/tests/nuageinit.sh | 46 +++++++++----------
 2 files changed, 53 insertions(+), 82 deletions(-)

diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
index 9a0399ad4862..6e900e01df4e 100755
--- a/libexec/nuageinit/nuageinit
+++ b/libexec/nuageinit/nuageinit
@@ -240,37 +240,11 @@ local function nameservers(interface, obj)
        local resolvconf_conf_handler = open_resolvconf_conf()
 
        if obj.search then
-               local with_space = false
-
-               resolvconf_conf_handler:write('search_domains="')
-
-               for _, d in ipairs(obj.search) do
-                       if with_space then
-                               resolvconf_conf_handler:write(" " .. d)
-                       else
-                               resolvconf_conf_handler:write(d)
-                               with_space = true
-                       end
-               end
-
-               resolvconf_conf_handler:write('"\n')
+               resolvconf_conf_handler:write("search_domains=" .. 
nuage.shell_escape(table.concat(obj.search, " ")) .. "\n")
        end
 
        if obj.addresses then
-               local with_space = false
-
-               resolvconf_conf_handler:write('name_servers="')
-
-               for _, a in ipairs(obj.addresses) do
-                       if with_space then
-                               resolvconf_conf_handler:write(" " .. a)
-                       else
-                               resolvconf_conf_handler:write(a)
-                               with_space = true
-                       end
-               end
-
-               resolvconf_conf_handler:write('"\n')
+               resolvconf_conf_handler:write("name_servers=" .. 
nuage.shell_escape(table.concat(obj.addresses, " ")) .. "\n")
        end
 
        resolvconf_conf_handler:close()
@@ -455,18 +429,18 @@ local function network_config(obj)
                                local ifaces = get_ifaces_by_mac()
                                local matched = ifaces[v.match.macaddress]
                                if matched and matched == interface then
-                                       network:write("ifconfig_" .. interface 
.. '_name=' .. v["set-name"] .. '\n')
+                                       network:write("ifconfig_" .. interface 
.. "_name=" .. nuage.shell_escape(v["set-name"]) .. "\n")
                                        interface = v["set-name"]
                                end
                        end
                        if v.dhcp4 then
-                               network:write("ifconfig_" .. interface .. 
'="DHCP"' .. extra_opts .. '\n')
+                               network:write("ifconfig_" .. interface .. "=" 
.. nuage.shell_escape("DHCP" .. extra_opts) .. "\n")
                        elseif v.addresses then
                                for _, a in pairs(v.addresses) do
                                        if 
a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then
-                                               network:write("ifconfig_" .. 
interface .. '="inet ' .. a .. extra_opts .. '"\n')
+                                               network:write("ifconfig_" .. 
interface .. "=" .. nuage.shell_escape("inet " .. a .. extra_opts) .. "\n")
                                        else
-                                               network:write("ifconfig_" .. 
interface .. '_ipv6="inet6 ' .. a .. extra_opts .. '"\n')
+                                               network:write("ifconfig_" .. 
interface .. "_ipv6=" .. nuage.shell_escape("inet6 " .. a .. extra_opts) .. 
"\n")
                                                ipv6[#ipv6 + 1] = interface
                                        end
                                end
@@ -476,24 +450,22 @@ local function network_config(obj)
                                end
                                if set_defaultrouter and v.gateway4 then
                                        set_defaultrouter = false
-                                       routing:write('defaultrouter="' .. 
v.gateway4 .. '"\n')
+                                       routing:write("defaultrouter=" .. 
nuage.shell_escape(v.gateway4) .. "\n")
                                end
                                if v.gateway6 then
                                        if set_defaultrouter6 then
                                                set_defaultrouter6 = false
-                                               
routing:write('ipv6_defaultrouter="' .. v.gateway6 .. '"\n')
+                                               
routing:write("ipv6_defaultrouter=" .. nuage.shell_escape(v.gateway6) .. "\n")
                                        end
-                                       routing:write("ipv6_route_" .. 
interface .. '="' .. v.gateway6)
-                                       routing:write(" -prefixlen 128 
-interface " .. interface .. '"\n')
+                                       routing:write("ipv6_route_" .. 
interface .. "=" .. nuage.shell_escape(v.gateway6 .. " -prefixlen 128 
-interface " .. interface) .. "\n")
                                end
                        end
                end
                ::next::
        end
        if #ipv6 > 0 then
-               network:write('ipv6_network_interfaces="')
-               network:write(table.concat(ipv6, " ") .. '"\n')
-               network:write('ipv6_default_interface="' .. ipv6[1] .. '"\n')
+               network:write("ipv6_network_interfaces=" .. 
nuage.shell_escape(table.concat(ipv6, " ")) .. "\n")
+               network:write("ipv6_default_interface=" .. 
nuage.shell_escape(ipv6[1]) .. "\n")
        end
        network:close()
        routing:close()
@@ -633,7 +605,7 @@ local function keyboard(obj)
                warnmsg("unable to open " .. path .. " for writing")
                return
        end
-       f:write('keymap="' .. keymap .. '"\n')
+       f:write("keymap=" .. nuage.shell_escape(keymap) .. "\n")
        f:close()
 end
 
@@ -648,10 +620,14 @@ local function locale(obj)
                return
        end
        if type(obj.locale) == "string" then
-               f:write("export LANG=" .. obj.locale .. "\n")
+               f:write("export LANG=" .. nuage.shell_escape(obj.locale) .. 
"\n")
        elseif type(obj.locale) == "table" then
                for k, v in pairs(obj.locale) do
-                       f:write("export " .. k .. "=" .. v .. "\n")
+                       if not k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
+                               nuage.warn("locale: invalid variable name '" .. 
k .. "', skipping")
+                       else
+                               f:write("export " .. k .. "=" .. 
nuage.shell_escape(v) .. "\n")
+                       end
                end
        else
                nuage.warn("locale: invalid type " .. type(obj.locale) .. ", 
expecting string or object")
@@ -920,14 +896,14 @@ local function config2_network(p)
        for _, v in pairs(obj["networks"]) do
                local interface = mylinks[v["link"]]
                if v["type"] == "ipv4_dhcp" then
-                       network:write("ifconfig_" .. interface .. '="DHCP"\n')
+                       network:write("ifconfig_" .. interface .. "=" .. 
nuage.shell_escape("DHCP") .. "\n")
                end
                if v["type"] == "ipv4" then
                        network:write(
-                               "ifconfig_" .. interface .. '="inet ' .. 
v["ip_address"] .. " netmask " .. v["netmask"] .. '"\n'
+                               "ifconfig_" .. interface .. "=" .. 
nuage.shell_escape("inet " .. v["ip_address"] .. " netmask " .. v["netmask"]) 
.. "\n"
                        )
                        if v["gateway"] then
-                               routing:write('defaultrouter="' .. v["gateway"] 
.. '"\n')
+                               routing:write("defaultrouter=" .. 
nuage.shell_escape(v["gateway"]) .. "\n")
                        end
                        if v["routes"] then
                                for i, r in ipairs(v["routes"]) do
@@ -936,11 +912,10 @@ local function config2_network(p)
                                                goto next
                                        end
                                        if r["network"] == "0.0.0.0" then
-                                               routing:write('defaultrouter="' 
.. r["gateway"] .. '"\n')
+                                               routing:write("defaultrouter=" 
.. nuage.shell_escape(r["gateway"]) .. "\n")
                                                goto next
                                        end
-                                       routing:write("route_" .. rname .. 
'="-net ' .. r["network"] .. " ")
-                                       routing:write(r["gateway"] .. " " .. 
r["netmask"] .. '"\n')
+                                       routing:write("route_" .. rname .. "=" 
.. nuage.shell_escape("-net " .. r["network"] .. " " .. r["gateway"] .. " " .. 
r["netmask"]) .. "\n")
                                        ipv4[#ipv4 + 1] = rname
                                        ::next::
                                end
@@ -949,11 +924,10 @@ local function config2_network(p)
                if v["type"] == "ipv6" then
                        ipv6[#ipv6 + 1] = interface
                        ipv6_routes[#ipv6_routes + 1] = interface
-                       network:write("ifconfig_" .. interface .. '_ipv6="inet6 
' .. v["ip_address"] .. '"\n')
+                       network:write("ifconfig_" .. interface .. "_ipv6=" .. 
nuage.shell_escape("inet6 " .. v["ip_address"]) .. "\n")
                        if v["gateway"] then
-                               routing:write('ipv6_defaultrouter="' .. 
v["gateway"] .. '"\n')
-                               routing:write("ipv6_route_" .. interface .. 
'="' .. v["gateway"])
-                               routing:write(" -prefixlen 128 -interface " .. 
interface .. '"\n')
+                               routing:write("ipv6_defaultrouter=" .. 
nuage.shell_escape(v["gateway"]) .. "\n")
+                               routing:write("ipv6_route_" .. interface .. "=" 
.. nuage.shell_escape(v["gateway"] .. " -prefixlen 128 -interface " .. 
interface) .. "\n")
                        end
                        -- TODO compute the prefixlen for the routes
                        --if v["routes"] then
@@ -988,17 +962,14 @@ local function config2_network(p)
        end
 
        if #ipv4 > 0 then
-               routing:write('static_routes="')
-               routing:write(table.concat(ipv4, " ") .. '"\n')
+               routing:write("static_routes=" .. 
nuage.shell_escape(table.concat(ipv4, " ")) .. "\n")
        end
        if #ipv6 > 0 then
-               network:write('ipv6_network_interfaces="')
-               network:write(table.concat(ipv6, " ") .. '"\n')
-               network:write('ipv6_default_interface="' .. ipv6[1] .. '"\n')
+               network:write("ipv6_network_interfaces=" .. 
nuage.shell_escape(table.concat(ipv6, " ")) .. "\n")
+               network:write("ipv6_default_interface=" .. 
nuage.shell_escape(ipv6[1]) .. "\n")
        end
        if #ipv6_routes > 0 then
-               routing:write('ipv6_static_routes="')
-               routing:write(table.concat(ipv6, " ") .. '"\n')
+               routing:write("ipv6_static_routes=" .. 
nuage.shell_escape(table.concat(ipv6, " ")) .. "\n")
        end
        network:close()
        routing:close()
diff --git a/libexec/nuageinit/tests/nuageinit.sh 
b/libexec/nuageinit/tests/nuageinit.sh
index 3f3e2843c35d..ce574a350ecc 100644
--- a/libexec/nuageinit/tests/nuageinit.sh
+++ b/libexec/nuageinit/tests/nuageinit.sh
@@ -223,15 +223,15 @@ network:
 EOF
        atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud
        cat > network << EOF
-ifconfig_${myiface}="inet 192.0.2.2/24"
-ifconfig_${myiface}_ipv6="inet6 2001:db8::2/64"
-ipv6_network_interfaces="${myiface}"
-ipv6_default_interface="${myiface}"
+ifconfig_${myiface}='inet 192.0.2.2/24'
+ifconfig_${myiface}_ipv6='inet6 2001:db8::2/64'
+ipv6_network_interfaces='${myiface}'
+ipv6_default_interface='${myiface}'
 EOF
        cat > routing << EOF
-defaultrouter="192.0.2.1"
-ipv6_defaultrouter="2001:db8::1"
-ipv6_route_${myiface}="2001:db8::1 -prefixlen 128 -interface ${myiface}"
+defaultrouter='192.0.2.1'
+ipv6_defaultrouter='2001:db8::1'
+ipv6_route_${myiface}='2001:db8::1 -prefixlen 128 -interface ${myiface}'
 EOF
        atf_check -o file:network cat "${PWD}"/etc/rc.conf.d/network
        atf_check -o file:routing cat "${PWD}"/etc/rc.conf.d/routing
@@ -406,15 +406,15 @@ cat > media/nuageinit/network_data.json << EOF
 EOF
        atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
        cat > network << EOF
-ifconfig_${myiface}="DHCP"
-ifconfig_${myiface}_ipv6="inet6 2001:db8::3257:9652/64"
-ipv6_network_interfaces="${myiface}"
-ipv6_default_interface="${myiface}"
+ifconfig_${myiface}='DHCP'
+ifconfig_${myiface}_ipv6='inet6 2001:db8::3257:9652/64'
+ipv6_network_interfaces='${myiface}'
+ipv6_default_interface='${myiface}'
 EOF
        cat > routing << EOF
-ipv6_defaultrouter="fd00::1"
-ipv6_route_${myiface}="fd00::1 -prefixlen 128 -interface ${myiface}"
-ipv6_static_routes="${myiface}"
+ipv6_defaultrouter='fd00::1'
+ipv6_route_${myiface}='fd00::1 -prefixlen 128 -interface ${myiface}'
+ipv6_static_routes='${myiface}'
 EOF
        atf_check -o file:network cat "${PWD}"/etc/rc.conf.d/network
        atf_check -o file:routing cat "${PWD}"/etc/rc.conf.d/routing
@@ -466,12 +466,12 @@ cat > media/nuageinit/network_data.json << EOF
 EOF
        atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
        cat > network << EOF
-ifconfig_${myiface}="inet 10.184.0.244 netmask 255.255.240.0"
+ifconfig_${myiface}='inet 10.184.0.244 netmask 255.255.240.0'
 EOF
        cat > routing << EOF
-route_cloudinit1_${myiface}="-net 10.0.0.0 11.0.0.1 255.0.0.0"
-defaultrouter="23.253.157.1"
-static_routes="cloudinit1_${myiface}"
+route_cloudinit1_${myiface}='-net 10.0.0.0 11.0.0.1 255.0.0.0'
+defaultrouter='23.253.157.1'
+static_routes='cloudinit1_${myiface}'
 EOF
        atf_check -o file:network cat "${PWD}"/etc/rc.conf.d/network
        atf_check -o file:routing cat "${PWD}"/etc/rc.conf.d/routing
@@ -518,7 +518,7 @@ cat > media/nuageinit/network_data.json << EOF
 }
 EOF
        atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit config-2
-       atf_check -o inline:'name_servers="9.9.9.9 149.112.112.112"\n' \
+       atf_check -o inline:"name_servers='9.9.9.9 149.112.112.112'\n" \
                cat "${PWD}"/etc/resolvconf.conf
 }
 
@@ -1203,7 +1203,7 @@ keyboard:
   variant: acc
 EOF
        atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit 
config-2
-       atf_check -o inline:'keymap="fr.acc"\n' cat etc/rc.conf.d/keymap
+       atf_check -o inline:"keymap='fr.acc'\n" cat etc/rc.conf.d/keymap
        true
 }
 
@@ -1351,7 +1351,7 @@ config2_userdata_locale_body()
 locale: fr_FR.UTF-8
 EOF
        atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit 
config-2
-       atf_check -o inline:"export LANG=fr_FR.UTF-8\n" cat etc/profile
+       atf_check -o inline:"export LANG='fr_FR.UTF-8'\n" cat etc/profile
 
        cat > media/nuageinit/user_data <<EOF
 #cloud-config
@@ -1360,8 +1360,8 @@ locale:
   LC_ALL: de_DE.UTF-8
 EOF
        atf_check -o empty /usr/libexec/nuageinit "${PWD}"/media/nuageinit 
config-2
-       atf_check -o match:"export LANG=de_DE.UTF-8" cat etc/profile
-       atf_check -o match:"export LC_ALL=de_DE.UTF-8" cat etc/profile
+       atf_check -o match:"export LANG='de_DE.UTF-8'" cat etc/profile
+       atf_check -o match:"export LC_ALL='de_DE.UTF-8'" cat etc/profile
        true
 }
 

Reply via email to