* [PATCH net 0/2] ieee802154: admin-gate legacy LLSEC dumps + un-deaden ADD/DEL
@ 2026-05-20 14:16 Michael Bommarito
2026-05-20 14:16 ` [PATCH net 1/2] ieee802154: admin-gate legacy LLSEC dump operations Michael Bommarito
2026-05-20 14:16 ` [PATCH net 2/2] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation Michael Bommarito
0 siblings, 2 replies; 3+ messages in thread
From: Michael Bommarito @ 2026-05-20 14:16 UTC (permalink / raw)
To: Alexander Aring, Stefan Schmidt, Miquel Raynal
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Phoebe Buckheister, linux-wpan, netdev,
linux-kernel
The legacy IEEE802154_NL family (net/ieee802154/netlink.c) builds its
ops table from two macros in net/ieee802154/ieee802154.h. IEEE802154_OP()
sets .flags = GENL_ADMIN_PERM; IEEE802154_DUMP() sets no flags. Among
the IEEE802154_DUMP() consumers are four LLSEC dump ops (LIST_KEY,
LIST_DEV, LIST_DEVKEY, LIST_SECLEVEL), and the LLSEC_LIST_KEY dump
handler at net/ieee802154/nl-mac.c emits the raw 16-byte AES-128
keytable bytes (IEEE802154_ATTR_LLSEC_KEY_BYTES, .len = 16, copied
verbatim from struct ieee802154_llsec_key.key) into the reply skb.
The modern nl802154 family admin-gates the equivalent reads
(NL802154_CMD_GET_SEC_KEY at net/ieee802154/nl802154.c:2978 with
.flags = GENL_ADMIN_PERM) so the legacy interface is the open side.
Any local uid with no capabilities can dump the AES-128 LLSEC keytable
of any wpan netdev that has an administrator-installed key. 802.15.4
LLSEC uses CCM*, so the same key authenticates and encrypts frames at
the chosen security level. One key leak compromises confidentiality
and authenticity at the link layer.
Patches:
1/2 ieee802154: admin-gate legacy LLSEC dump operations
Introduce IEEE802154_DUMP_PRIV() mirroring IEEE802154_DUMP() but
with .flags = GENL_ADMIN_PERM. Switch the four LLSEC dump ops to
it. LIST_PHY and LIST_IFACE keep IEEE802154_DUMP() because the
modern nl802154 family exposes their equivalents to unprivileged
readers by design.
2/2 ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation
While reproducing the dump leak the LLSEC ADD/DEL doit handlers
turned out to have been silently dead since strict netlink
validation became the default: IEEE802154_ATTR_LLSEC_KEY_BYTES
and _KEY_USAGE_COMMANDS are declared as NLA_UNSPEC in
nl_policy.c, which validate_nla() rejects under
NL_VALIDATE_STRICT. Add IEEE802154_OP_RELAXED() with
.validate = GENL_DONT_VALIDATE_STRICT and switch the eight LLSEC
mutate ops to it. The dump ops keep strict validation; their
gate from patch 1/2 stands.
This would be a good time to revisit this thread from Alexander Aring:
https://lore.kernel.org/r/20160725121450.4093-4-aar@pengutronix.de
Ten years later the legacy interface is still in mainline and
(per patch 2/2) silently dead on the doit side anyway. Maintainers
who prefer to revive that deletion series should NACK patch 2/2 and
pick patch 1/2 alone; patch 1/2 plugs the key leak whether the
legacy doit path is ever brought back to life or removed entirely.
Reproduction
============
Tree: mainline 7.1-rc2 (22be7671cc3497f68d14555285c0ac221597c8eb),
x86 UML, KASAN off. Conditions: CONFIG_IEEE802154=y, CONFIG_MAC802154=y,
CONFIG_IEEE802154_HWSIM=y; one wpan netdev exists; an administrator
has installed an LLSEC key on it.
Harness: a userspace C program opens AF_NETLINK / NETLINK_GENERIC,
resolves the "802.15.4 MAC" family, and issues a CTRL_CMD_GETFAMILY
followed by a IEEE802154_LLSEC_LIST_KEY dump on wpan0. The dump
response is parsed for IEEE802154_ATTR_LLSEC_KEY_BYTES nested
attributes and the 16-byte payload is hex-printed. The runner drops
privileges to uid=1000 via setpriv with --bounding-set=-all and
--inh-caps=-all before invoking the dump.
For patch 2/2, the same harness shape sends a IEEE802154_LLSEC_ADD_KEY
request with policy-conformant attributes (frame counter, key ID
mode, key bytes, usage frame types) as root. The harness measures
whether the doit handler is reached (ieee802154_llsec_add_key()
return code visible via the netlink ack) or the dispatcher rejects
the request at the validate phase.
Stock vs patched outcomes (per patch):
1/2: stock dump returns the installed AES-128 key bytes
byte-identical to the unprivileged dumper; patched returns
-EPERM at the generic-netlink dispatch.
2/2: stock LLSEC_ADD_KEY as root returns "Unsupported attribute"
from __nla_validate_parse(); patched reaches
ieee802154_llsec_add_key() and installs the key.
Regression-adjacent runs (per patch):
1/2: LIST_PHY and LIST_IFACE dumps continue to succeed from an
unprivileged uid post-patch, mirroring the modern
NL802154_CMD_GET_WPAN_PHY and NL802154_CMD_GET_INTERFACE
behavior.
2/2: modern nl802154 NL802154_CMD_NEW_SEC_KEY (which is admin-
gated and uses NLA_NESTED policy entries, not NLA_UNSPEC)
continues to install keys unchanged.
Mitigations: a system administrator unwilling to ship patch 1/2
can mitigate the leak by building the kernel without
CONFIG_IEEE802154 or by ensuring no LLSEC key is installed on
any wpan netdev. There is no per-process mitigation.
Selftest gate: tools/testing/selftests/ has no matching binary
for the legacy IEEE802154_NL interface.
The trigger harness sources are available off-list on request.
Scope notes:
- The dump handler reuses the same struct ieee802154_llsec_key
storage as the modern nl802154 NEW_SEC_KEY path. Both interfaces
expose the same backing keytable; gating only the legacy dump is
the minimal fix.
- The two new macros sit alongside the existing two; no existing
callers change beyond the four LIST_LLSEC* lines (patch 1/2)
and the eight LLSEC ADD/DEL lines (patch 2/2).
Michael Bommarito (2):
ieee802154: admin-gate legacy LLSEC dump operations
ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation
net/ieee802154/ieee802154.h | 17 +++++++++++++++++
net/ieee802154/netlink.c | 36 ++++++++++++++++++------------------
2 files changed, 35 insertions(+), 18 deletions(-)
base-commit: 22be7671cc3497f68d14555285c0ac221597c8eb
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net 1/2] ieee802154: admin-gate legacy LLSEC dump operations
2026-05-20 14:16 [PATCH net 0/2] ieee802154: admin-gate legacy LLSEC dumps + un-deaden ADD/DEL Michael Bommarito
@ 2026-05-20 14:16 ` Michael Bommarito
2026-05-20 14:16 ` [PATCH net 2/2] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation Michael Bommarito
1 sibling, 0 replies; 3+ messages in thread
From: Michael Bommarito @ 2026-05-20 14:16 UTC (permalink / raw)
To: Alexander Aring, Stefan Schmidt, Miquel Raynal
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Phoebe Buckheister, linux-wpan, netdev,
linux-kernel
In net/ieee802154/netlink.c, the legacy IEEE802154_NL family ops table
builds the LLSEC dump entries (LLSEC_LIST_KEY, LLSEC_LIST_DEV,
LLSEC_LIST_DEVKEY, LLSEC_LIST_SECLEVEL) with IEEE802154_DUMP() which
sets no .flags, so generic netlink runs them ungated. The modern
nl802154 family admin-gates the equivalent reads via
NL802154_CMD_GET_SEC_KEY and friends with .flags = GENL_ADMIN_PERM.
Any local uid that can open AF_NETLINK / NETLINK_GENERIC can resolve
the "802.15.4 MAC" family and dump LLSEC_LIST_KEY on any wpan netdev
that has an LLSEC key installed; the dump handler writes the raw
16-byte AES-128 key bytes (IEEE802154_ATTR_LLSEC_KEY_BYTES, copied
verbatim from struct ieee802154_llsec_key.key) into the reply.
Recovering the AES key compromises 802.15.4 LLSEC link confidentiality
and authenticity, since LLSEC uses CCM* and the same key authenticates
and encrypts frames.
Impact: any local uid with no capabilities can read the raw 16-byte
AES-128 LLSEC key from the kernel keytable on any wpan netdev that has
an administrator-installed LLSEC key, by issuing an LLSEC_LIST_KEY
dump on the legacy IEEE802154_NL generic-netlink family.
Introduce IEEE802154_DUMP_PRIV() mirroring IEEE802154_DUMP() but
setting .flags = GENL_ADMIN_PERM, and use it for the four LLSEC dump
entries. LIST_PHY and LIST_IFACE retain IEEE802154_DUMP() because the
modern nl802154 family exposes their equivalents to unprivileged
readers by design (NL802154_CMD_GET_WPAN_PHY and
NL802154_CMD_GET_INTERFACE carry "can be retrieved by unprivileged
users" annotations).
Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
net/ieee802154/ieee802154.h | 8 ++++++++
net/ieee802154/netlink.c | 16 ++++++++--------
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index c5d91f78301ad..fd9778f705503 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -23,6 +23,14 @@ void ieee802154_nl_exit(void);
.dumpit = _dump, \
}
+#define IEEE802154_DUMP_PRIV(_cmd, _func, _dump) \
+ { \
+ .cmd = _cmd, \
+ .doit = _func, \
+ .dumpit = _dump, \
+ .flags = GENL_ADMIN_PERM, \
+ }
+
struct genl_info;
struct sk_buff *ieee802154_nl_create(int flags, u8 req);
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 7d2de4ee6992b..9c9fd14d0ca8b 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -98,20 +98,20 @@ static const struct genl_small_ops ieee802154_ops[] = {
IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams),
IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams),
IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL,
- ieee802154_llsec_dump_keys),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL,
+ ieee802154_llsec_dump_keys),
IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL,
- ieee802154_llsec_dump_devs),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL,
+ ieee802154_llsec_dump_devs),
IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
- ieee802154_llsec_dump_devkeys),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
+ ieee802154_llsec_dump_devkeys),
IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
- ieee802154_llsec_dump_seclevels),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
+ ieee802154_llsec_dump_seclevels),
IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL,
ieee802154_llsec_add_seclevel),
IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL,
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH net 2/2] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation
2026-05-20 14:16 [PATCH net 0/2] ieee802154: admin-gate legacy LLSEC dumps + un-deaden ADD/DEL Michael Bommarito
2026-05-20 14:16 ` [PATCH net 1/2] ieee802154: admin-gate legacy LLSEC dump operations Michael Bommarito
@ 2026-05-20 14:16 ` Michael Bommarito
1 sibling, 0 replies; 3+ messages in thread
From: Michael Bommarito @ 2026-05-20 14:16 UTC (permalink / raw)
To: Alexander Aring, Stefan Schmidt, Miquel Raynal
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Phoebe Buckheister, linux-wpan, netdev,
linux-kernel
The LLSEC ADD/DEL doit handlers under the legacy IEEE802154_NL family
consume IEEE802154_ATTR_LLSEC_KEY_BYTES and
IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS, both declared in
net/ieee802154/nl_policy.c as bare length entries with no .type
(defaulting to NLA_UNSPEC). Generic netlink strict validation rejects
all NLA_UNSPEC attributes via validate_nla(), so every LLSEC_ADD_KEY,
LLSEC_DEL_KEY, LLSEC_ADD_DEV, LLSEC_DEL_DEV, LLSEC_ADD_DEVKEY,
LLSEC_DEL_DEVKEY, LLSEC_ADD_SECLEVEL, and LLSEC_DEL_SECLEVEL request
fails at the dispatcher with "Unsupported attribute" before reaching
the handler.
The doit path has been silently dead since strict validation became
the default for genl families that do not opt out. The dump path is
unaffected because dump requests carry no LLSEC attributes to
validate, which is why the LLSEC_LIST_KEY read remained reachable
(patch 1/2). Introduce IEEE802154_OP_RELAXED() mirroring
IEEE802154_OP() but with .validate = GENL_DONT_VALIDATE_STRICT, and
use it for the eight legacy LLSEC mutate ops so admin-driven LLSEC
configuration via the legacy interface works again.
Fixes: 3e9c156e2c21 ("ieee802154: add netlink interfaces for llsec")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
net/ieee802154/ieee802154.h | 9 +++++++++
net/ieee802154/netlink.c | 20 ++++++++++----------
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index fd9778f705503..e765adc4b88f2 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -16,6 +16,15 @@ void ieee802154_nl_exit(void);
.flags = GENL_ADMIN_PERM, \
}
+#define IEEE802154_OP_RELAXED(_cmd, _func) \
+ { \
+ .cmd = _cmd, \
+ .doit = _func, \
+ .dumpit = NULL, \
+ .flags = GENL_ADMIN_PERM, \
+ .validate = GENL_DONT_VALIDATE_STRICT,\
+ }
+
#define IEEE802154_DUMP(_cmd, _func, _dump) \
{ \
.cmd = _cmd, \
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 9c9fd14d0ca8b..cacad21347eca 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -100,22 +100,22 @@ static const struct genl_small_ops ieee802154_ops[] = {
IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams),
IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL,
ieee802154_llsec_dump_keys),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL,
ieee802154_llsec_dump_devs),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
ieee802154_llsec_dump_devkeys),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
ieee802154_llsec_dump_seclevels),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL,
- ieee802154_llsec_add_seclevel),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL,
- ieee802154_llsec_del_seclevel),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_SECLEVEL,
+ ieee802154_llsec_add_seclevel),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_SECLEVEL,
+ ieee802154_llsec_del_seclevel),
};
static const struct genl_multicast_group ieee802154_mcgrps[] = {
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-20 14:16 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20 14:16 [PATCH net 0/2] ieee802154: admin-gate legacy LLSEC dumps + un-deaden ADD/DEL Michael Bommarito
2026-05-20 14:16 ` [PATCH net 1/2] ieee802154: admin-gate legacy LLSEC dump operations Michael Bommarito
2026-05-20 14:16 ` [PATCH net 2/2] ieee802154: allow legacy LLSEC ADD/DEL ops to pass strict validation Michael Bommarito
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox