diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md index 9f50875a1..8af796d83 100644 --- a/doc/ChangeLog.md +++ b/doc/ChangeLog.md @@ -103,6 +103,13 @@ All notable changes to the project are documented in this file. ### Fixes +- Constrain Wi-Fi mesh-id and NAS identifier, and validate access-point and mesh passphrases as strictly as station +- Apply syslog configuration changes at runtime, not only after reboot +- Restrict the allowed characters in keystore key and certificate names +- Reject control characters and double quotes in syslog property-filter value and pattern-match +- Reject control characters and commas in DHCP server static-host match values +- Restrict the allowed characters in a container `command` override +- Restrict the allowed characters in a hardware component `name` - Fix #1619: Raspberry Pi kernel panic when configure Wi-Fi - WebUI: "Save" in the interface editor and "OK" in Add Interface did nothing for Wi-Fi and WireGuard interfaces. The inline "+ New" diff --git a/src/confd/share/migrate/1.10/20-hardware-component-name.sh b/src/confd/share/migrate/1.10/20-hardware-component-name.sh new file mode 100755 index 000000000..ab82e4a38 --- /dev/null +++ b/src/confd/share/migrate/1.10/20-hardware-component-name.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# ietf-hardware component names are now restricted to a safe character set: +# letters, digits, '_', '.', '-' and '@', not starting with '.' or '-'. +# Sanitize any configured component name that would no longer validate +# and update the leaves that reference it: the Wi-Fi radio of an interface +# and the GPS receiver of an NTP reference-clock source. Probed names +# (radioN, gpsN) never match, so this is a safety net for hand-edited +# configs. + +file=$1 +temp=${file}.tmp + +# Everything the identifier type does not allow. +bad='[^a-zA-Z0-9_.@-]' + +jq --arg bad "$bad" ' + ["ietf-hardware:hardware", "component"] as $hw + | if getpath($hw) == null then . else + ( [ getpath($hw)[] + | (.name | gsub($bad; "") | sub("^[.-]+"; "")) as $new + | select($new != .name and $new != "") + | { key: .name, value: $new } ] + | from_entries ) as $ren + | if ($ren | length) == 0 then . else + setpath($hw; getpath($hw) + | map(if $ren[.name] != null then .name = $ren[.name] else . end)) + | ["ietf-interfaces:interfaces", "interface"] as $ifs + | (if getpath($ifs) != null then + setpath($ifs; getpath($ifs) | map( + ((.["infix-interfaces:wifi"] // {}) | .radio // null) as $r + | if $r != null and $ren[$r] != null + then .["infix-interfaces:wifi"].radio = $ren[$r] + else . end)) + else . end) + | ["ietf-ntp:ntp", "refclock-master", "infix-ntp:source"] as $src + | (if getpath($src) != null then + setpath($src; getpath($src) | map( + if $ren[.receiver] != null then .receiver = $ren[.receiver] else . end)) + else . end) + end + end +' "$file" > "$temp" && mv "$temp" "$file" diff --git a/src/confd/share/migrate/1.10/Makefile.am b/src/confd/share/migrate/1.10/Makefile.am index 07782eedc..6ae1d50c9 100644 --- a/src/confd/share/migrate/1.10/Makefile.am +++ b/src/confd/share/migrate/1.10/Makefile.am @@ -1,2 +1,2 @@ migratedir = $(pkgdatadir)/migrate/1.10 -dist_migrate_DATA = 10-software-update-url.sh +dist_migrate_DATA = 10-software-update-url.sh 20-hardware-component-name.sh diff --git a/src/confd/src/core.c b/src/confd/src/core.c index 86343ae82..bd705cce5 100644 --- a/src/confd/src/core.c +++ b/src/confd/src/core.c @@ -874,6 +874,11 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv) ERROR("Failed to subscribe to ietf-system"); goto err; } + rc = subscribe_model("ietf-syslog", &confd, 0); + if (rc) { + ERROR("Failed to subscribe to ietf-syslog"); + goto err; + } rc = subscribe_model("ieee802-dot1ab-lldp", &confd, 0); if (rc) { ERROR("Failed to subscribe to ieee802-dot1ab-lldp"); diff --git a/src/confd/src/if-wifi.c b/src/confd/src/if-wifi.c index 84653dfa1..8b9fca53a 100644 --- a/src/confd/src/if-wifi.c +++ b/src/confd/src/if-wifi.c @@ -18,27 +18,28 @@ #define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf" -int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif) +/* + * Validate one wifi security block's referenced keystore secret. The + * decoded passphrase is written verbatim into wpa_supplicant/hostapd + * config, so it must be 8-63 printable characters (no newline, which + * would inject an unrelated directive). Applies to station, access + * point and mesh alike. + */ +static int validate_wifi_secret(sr_session_ctx_t *session, const char *ifname, + struct lyd_node *cif, struct lyd_node *security) { - struct lyd_node *wifi, *station, *security, *secret_node; - const char *ifname, *secret_name, *security_mode, *b64; + const char *secret_name, *security_mode, *b64; + struct lyd_node *secret_node; unsigned char *decoded; size_t len; - ifname = lydx_get_cattr(cif, "name"); - wifi = lydx_get_child(cif, "wifi"); - if (!wifi) - return SR_ERR_OK; - - station = lydx_get_child(wifi, "station"); - if (!station) + if (!security) return SR_ERR_OK; - security = lydx_get_child(station, "security"); security_mode = lydx_get_cattr(security, "mode"); secret_name = lydx_get_cattr(security, "secret"); - if (!secret_name || !strcmp(security_mode, "disabled")) + if (!secret_name || (security_mode && !strcmp(security_mode, "disabled"))) return SR_ERR_OK; secret_node = lydx_get_xpathf(cif, @@ -77,6 +78,35 @@ int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif) return SR_ERR_OK; } +int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif) +{ + static const char *const modes[] = { + "station", "access-point", "mesh-point" + }; + const char *ifname; + struct lyd_node *wifi; + + ifname = lydx_get_cattr(cif, "name"); + wifi = lydx_get_child(cif, "wifi"); + if (!wifi) + return SR_ERR_OK; + + for (size_t i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) { + struct lyd_node *node = lydx_get_child(wifi, modes[i]); + int rc; + + if (!node) + continue; + + rc = validate_wifi_secret(session, ifname, cif, + lydx_get_child(node, "security")); + if (rc) + return rc; + } + + return SR_ERR_OK; +} + wifi_mode_t wifi_get_mode(struct lyd_node *iface) { struct lyd_node *ap, *mesh, *wifi; diff --git a/src/confd/yang/confd.inc b/src/confd/yang/confd.inc index db00e3b45..8617694b4 100644 --- a/src/confd/yang/confd.inc +++ b/src/confd/yang/confd.inc @@ -24,10 +24,10 @@ MODULES=( # NOTE: ietf-tls-client must be version matched with ietf-tls-server, used by netopeer2! # "ietf-tls-client@2023-12-28.yang" "ietf-syslog@2024-03-21.yang -e file-action -e file-limit-size -e remote-action -e select-adv-compare -e select-match" - "infix-syslog@2026-09-15.yang" + "infix-syslog@2026-09-24.yang" "iana-hardware@2018-03-13.yang" "ietf-hardware@2018-03-13.yang -e hardware-state -e hardware-sensor" - "infix-hardware@2026-07-02.yang" + "infix-hardware@2026-09-24.yang" "ieee802-dot1q-types@2022-10-29.yang" "infix-ip@2026-04-28.yang" "infix-if-type@2026-01-07.yang" @@ -37,7 +37,7 @@ MODULES=( "infix-dhcp-common@2025-12-21.yang" "infix-dhcp-client@2025-11-09.yang" "infix-dhcpv6-client@2025-11-09.yang" - "infix-dhcp-server@2026-09-18.yang" + "infix-dhcp-server@2026-09-24.yang" "infix-firewall@2026-07-02.yang" "infix-firewall-services@2025-04-26.yang" "infix-firewall-icmp-types@2025-04-26.yang" @@ -53,7 +53,7 @@ MODULES=( "infix-crypto-types@2026-02-14.yang" "ietf-keystore -e symmetric-keys" "infix-ntp@2026-06-11.yang" - "infix-keystore@2025-12-17.yang" + "infix-keystore@2026-09-24.yang" "ieee1588-ptp-tt@2023-08-14.yang -e timestamp-correction" "ieee802-dot1as-gptp@2025-12-10.yang" "infix-ptp@2026-04-07.yang" diff --git a/src/confd/yang/confd/infix-containers.yang b/src/confd/yang/confd/infix-containers.yang index 401ed6437..bd17081d8 100644 --- a/src/confd/yang/confd/infix-containers.yang +++ b/src/confd/yang/confd/infix-containers.yang @@ -22,6 +22,14 @@ module infix-containers { prefix infix-sys; } + + revision 2026-09-24 { + description "Disallow shell expansion, quoting and command chaining + characters in the command override. Environment + variables are set with the env list."; + reference "internal"; + } + revision 2026-04-20 { description "Add cmdline operational leaf showing the full process command line (entrypoint + args) from 'podman inspect'. Allow environment variable @@ -264,7 +272,7 @@ module infix-containers { leaf command { description "Override ENTRYPOINT from image and run command + args."; type string { - pattern '[a-zA-Z0-9_./ :=@%^,${}()+-]+'; + pattern "[^\\p{Cc}\\\\;|&$`(){}<>*?!#~'\"\\[\\]]+"; } } diff --git a/src/confd/yang/confd/infix-containers@2026-04-20.yang b/src/confd/yang/confd/infix-containers@2026-09-24.yang similarity index 100% rename from src/confd/yang/confd/infix-containers@2026-04-20.yang rename to src/confd/yang/confd/infix-containers@2026-09-24.yang diff --git a/src/confd/yang/confd/infix-dhcp-server.yang b/src/confd/yang/confd/infix-dhcp-server.yang index 68f847cdd..7f9388bb0 100644 --- a/src/confd/yang/confd/infix-dhcp-server.yang +++ b/src/confd/yang/confd/infix-dhcp-server.yang @@ -20,6 +20,12 @@ module infix-dhcp-server { contact "kernelkit@googlegroups.com"; description "This module implements a DHCPv4 server"; + revision 2026-09-24 { + description "Constrain static-host match hostname and client-id string: + no control characters or commas, at most 255 bytes."; + reference "internal"; + } + revision 2026-09-18 { description "Add network boot parameters (BOOTP siaddr/file, option 66/67) at global, subnet, and host scope."; @@ -283,7 +289,13 @@ module infix-dhcp-server { case hostname { leaf hostname { description "Match on client hostname, DHCP option 12."; - type string; + reference "RFC 2132, sec. 3.14: Host Name Option. The + option length field is one octet, so the value + is at most 255 bytes."; + type string { + length "1..255"; + pattern '[^\p{Cc},]+'; + } } } @@ -298,7 +310,14 @@ module infix-dhcp-server { description "String value for text-based client-id. Example: xyzzy"; - type string; + reference "RFC 2132, sec. 9.14: Client-identifier + (option 61). The option length field is + one octet, so the value is at most 255 + bytes."; + type string { + length "1..255"; + pattern '[^\p{Cc},]+'; + } } } case hex { diff --git a/src/confd/yang/confd/infix-dhcp-server@2026-09-18.yang b/src/confd/yang/confd/infix-dhcp-server@2026-09-24.yang similarity index 100% rename from src/confd/yang/confd/infix-dhcp-server@2026-09-18.yang rename to src/confd/yang/confd/infix-dhcp-server@2026-09-24.yang diff --git a/src/confd/yang/confd/infix-hardware.yang b/src/confd/yang/confd/infix-hardware.yang index 630bf570e..7c98fd387 100644 --- a/src/confd/yang/confd/infix-hardware.yang +++ b/src/confd/yang/confd/infix-hardware.yang @@ -21,6 +21,12 @@ module infix-hardware { contact "kernelkit@googlegroups.com"; description "Vital Product Data augmentation of ieee-hardware and deviations."; + revision 2026-09-24 { + description "Constrain hardware component names to letters, digits, + '_', '.', '-' and '@', not starting with '.' or '-'."; + reference "internal"; + } + revision 2026-07-02 { description "Widen wifi max-interfaces ap/station to uint16, virtual radios (mac80211_hwsim) report combinations up to 2048."; @@ -163,6 +169,17 @@ module infix-hardware { description "GPS/GNSS receiver for time synchronization"; } + deviation "/iehw:hardware/iehw:component/iehw:name" { + deviate replace { + type string { + pattern '[a-zA-Z0-9_][a-zA-Z0-9_.@-]*'; + } + } + description "Component names are plain identifiers: letters, digits, + '_', '.', '-' and '@' (device-tree unit addresses such as + 'sfp@9'), not starting with '.' or '-'."; + } + deviation "/iehw:hardware/iehw:component/iehw:state/iehw:admin-state" { deviate add { must ". = 'locked' or . = 'unlocked'" { diff --git a/src/confd/yang/confd/infix-hardware@2026-07-02.yang b/src/confd/yang/confd/infix-hardware@2026-09-24.yang similarity index 100% rename from src/confd/yang/confd/infix-hardware@2026-07-02.yang rename to src/confd/yang/confd/infix-hardware@2026-09-24.yang diff --git a/src/confd/yang/confd/infix-if-wifi.yang b/src/confd/yang/confd/infix-if-wifi.yang index 4f037e54f..29a768d7b 100644 --- a/src/confd/yang/confd/infix-if-wifi.yang +++ b/src/confd/yang/confd/infix-if-wifi.yang @@ -48,6 +48,13 @@ submodule infix-if-wifi { - Security: WPA2/WPA3 with keystore integration - Operational state: Connection status, RSSI, client lists"; + revision 2026-09-24 { + description + "Constrain mesh-id to the SSID character set and nas-identifier + to letters, digits, '.', '_' and '-'."; + reference "internal"; + } + revision 2026-07-01 { description "Add station 'bssid' operational leaf: the BSSID the station is @@ -527,6 +534,7 @@ submodule infix-if-wifi { } type string { length "1..253"; + pattern '[a-zA-Z0-9._-]+'; } } default auto; @@ -697,6 +705,9 @@ submodule infix-if-wifi { leaf mesh-id { type string { length "1..32"; + pattern '[^\x00-\x1f\x22\x5c\x7f]*' { + error-message "Mesh ID must not contain control characters, double quotes, or backslashes."; + } } mandatory true; description diff --git a/src/confd/yang/confd/infix-if-wifi@2026-07-01.yang b/src/confd/yang/confd/infix-if-wifi@2026-09-24.yang similarity index 100% rename from src/confd/yang/confd/infix-if-wifi@2026-07-01.yang rename to src/confd/yang/confd/infix-if-wifi@2026-09-24.yang diff --git a/src/confd/yang/confd/infix-keystore.yang b/src/confd/yang/confd/infix-keystore.yang index c27fbcf81..e26773489 100644 --- a/src/confd/yang/confd/infix-keystore.yang +++ b/src/confd/yang/confd/infix-keystore.yang @@ -9,6 +9,13 @@ module infix-keystore { prefix infix-ct; } + revision 2026-09-24 { + description "Constrain asymmetric-key and certificate names: no control + characters, whitespace, '/', '\\' or shell metacharacters, + and no leading '.' or '-'."; + reference "internal"; + } + revision 2025-12-17 { description "Add WireGuard support, see infix-crypto-types.yang"; } @@ -21,4 +28,28 @@ module infix-keystore { revision 2025-02-04 { description "Initial"; } + + deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:name" { + deviate replace { + type string { + pattern "[^\\p{Cc}\\s/\\\\;|&$`(){}<>*?!#~'\"\\[\\]]+"; + pattern "[^.-].*"; + } + } + description "Key names may not contain control characters, whitespace, + '/', '\\' or any of ; | & $ ` ( ) { } < > * ? ! # ~ ' \" [ ], + and may not start with '.' or '-'."; + } + + deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:certificates/ks:certificate/ks:name" { + deviate replace { + type string { + pattern "[^\\p{Cc}\\s/\\\\;|&$`(){}<>*?!#~'\"\\[\\]]+"; + pattern "[^.-].*"; + } + } + description "Certificate names may not contain control characters, whitespace, + '/', '\\' or any of ; | & $ ` ( ) { } < > * ? ! # ~ ' \" [ ], + and may not start with '.' or '-'."; + } } diff --git a/src/confd/yang/confd/infix-keystore@2025-12-17.yang b/src/confd/yang/confd/infix-keystore@2026-09-24.yang similarity index 100% rename from src/confd/yang/confd/infix-keystore@2025-12-17.yang rename to src/confd/yang/confd/infix-keystore@2026-09-24.yang diff --git a/src/confd/yang/confd/infix-syslog.yang b/src/confd/yang/confd/infix-syslog.yang index b245d68b7..c9a3aff68 100644 --- a/src/confd/yang/confd/infix-syslog.yang +++ b/src/confd/yang/confd/infix-syslog.yang @@ -20,6 +20,12 @@ module infix-syslog { contact "kernelkit@googlegroups.com"; description "Infix augments and deviations to ietf-syslog, draft 32."; + revision 2026-09-24 { + description "Exclude control characters and the double quote from the + property-filter value and the select pattern-match."; + reference "internal"; + } + revision 2026-09-15 { description "Add log RPC for injecting messages in the system log."; reference "internal"; @@ -251,9 +257,12 @@ module infix-syslog { } leaf value { - type string; + type string { + pattern '[^\p{Cc}"]+'; + } mandatory true; - description "The value to compare against."; + description "The value to compare against. Control characters and + the double quote are not allowed."; } leaf case-insensitive { @@ -308,6 +317,26 @@ module infix-syslog { deviate not-supported; } + deviation "/syslog:syslog/syslog:actions/syslog:file/syslog:log-file/syslog:pattern-match" { + description "Control characters and the double quote are not allowed. + Regular-expression metacharacters remain allowed."; + deviate replace { + type string { + pattern '[^\p{Cc}"]+'; + } + } + } + + deviation "/syslog:syslog/syslog:actions/syslog:remote/syslog:destination/syslog:pattern-match" { + description "Control characters and the double quote are not allowed. + Regular-expression metacharacters remain allowed."; + deviate replace { + type string { + pattern '[^\p{Cc}"]+'; + } + } + } + /* * RPCs */ diff --git a/src/confd/yang/confd/infix-syslog@2026-09-15.yang b/src/confd/yang/confd/infix-syslog@2026-09-24.yang similarity index 100% rename from src/confd/yang/confd/infix-syslog@2026-09-15.yang rename to src/confd/yang/confd/infix-syslog@2026-09-24.yang diff --git a/src/confd/yang/confd/infix-system.yang b/src/confd/yang/confd/infix-system.yang index c3415ee11..5d902ac9c 100644 --- a/src/confd/yang/confd/infix-system.yang +++ b/src/confd/yang/confd/infix-system.yang @@ -383,6 +383,7 @@ module infix-system { augment "/sys:system" { description "Advanced, low-level system customization."; container advanced { + nacm:default-deny-write; description "Advanced system customization, for debugging and development. Settings in this container reach below the abstractions of the diff --git a/src/confd/yang/containers.inc b/src/confd/yang/containers.inc index 1daa36d31..6176f7cbd 100644 --- a/src/confd/yang/containers.inc +++ b/src/confd/yang/containers.inc @@ -1,5 +1,5 @@ # -*- sh -*- MODULES=( "infix-interfaces -e containers" - "infix-containers@2026-04-20.yang" + "infix-containers@2026-09-24.yang" )