Skip to content

Commit e8a44ee

Browse files
committed
fix(mqtt): preserve signed packet metadata
1 parent 11555d2 commit e8a44ee

1 file changed

Lines changed: 14 additions & 3 deletions

File tree

src/mqtt/MQTT.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
124124
if (!pAck)
125125
return;
126126
pAck->transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT;
127+
// sendLocal consumes packets sent to a live interface, but returns SHOULD_RELEASE
128+
// when it handled a local delivery synchronously. Match MeshService::sendToMesh's
129+
// ownership contract so the MQTT acknowledgement cannot leak on the local path.
127130
if (router->sendLocal(pAck) == ERRNO_SHOULD_RELEASE)
128131
packetPool.release(pAck);
129132
} else {
@@ -158,6 +161,11 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
158161

159162
if (shouldDropMqttDownlink(*p))
160163
return;
164+
// A decoded MQTT packet has already crossed the broker boundary, so authenticate it in place
165+
// before handing it to the router. The routing-auth cache intentionally keeps an authenticated
166+
// copy separate for encrypted packets, but a cache hit alone does not update this packet's
167+
// xeddsa_signed marker (which is part of the downstream message metadata).
168+
bool decodedAuthChecked = false;
161169

162170
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
163171
if (moduleConfig.mqtt.encryption_enabled) {
@@ -175,11 +183,14 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
175183
// protection for known signers. Without this, a peer on a plaintext broker could
176184
// impersonate a signing node with unsigned broadcasts. Hold cryptLock like the RF path
177185
// (perhapsDecode) does - checkXeddsaReceivePolicy -> xeddsa_verify mutates shared
178-
// CryptoEngine cache state, and MQTT ingress can run on a different task.
179-
if (passesRoutingAuthGate(p.get()) != RoutingAuthVerdict::ACCEPT) {
186+
// CryptoEngine cache state, and MQTT ingress can run on a different task. The in-place
187+
// call preserves the verified xeddsa_signed marker for downstream routing/UI consumers.
188+
concurrency::LockGuard g(cryptLock);
189+
if (!checkXeddsaReceivePolicy(p.get())) {
180190
LOG_INFO("Ignore decoded message failing XEdDSA policy");
181191
return;
182192
}
193+
decodedAuthChecked = true;
183194
#endif
184195
}
185196

@@ -191,7 +202,7 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
191202
// likely they discovered each other via a channel we have downlink enabled for
192203
if (isToUs(p.get()) || (nodeInfoLiteHasUser(tx) && nodeInfoLiteHasUser(rx)))
193204
router->enqueueReceivedMessage(p.release());
194-
} else if (router && passesRoutingAuthGate(p.get()) == RoutingAuthVerdict::ACCEPT)
205+
} else if (router && (decodedAuthChecked || passesRoutingAuthGate(p.get()) == RoutingAuthVerdict::ACCEPT))
195206
router->enqueueReceivedMessage(p.release());
196207
}
197208

0 commit comments

Comments
 (0)