Skip to main content

bacnet_server/
audit_notification.rs

1//! Receiver policy for confirmed and unconfirmed AuditNotification services.
2
3use bacnet_encoding::npdu::NpduAddress;
4use bacnet_services::audit::AuditNotificationRequest;
5use bacnet_transport::port::TransportProvenance;
6use bacnet_types::primitives::ObjectIdentifier;
7use bacnet_types::MacAddr;
8
9/// Local maximum accepted AuditNotification service payload.
10pub const MAX_AUDIT_NOTIFICATION_BYTES: usize = 64 * 1024;
11/// Local maximum number of notifications in one accepted request.
12pub const MAX_AUDIT_NOTIFICATIONS: usize = 256;
13
14/// Transport provenance and decoded content supplied to
15/// [`ServerPolicy::authorize_audit_notification`](crate::ServerPolicy::authorize_audit_notification).
16///
17/// The source/target identities inside `request` are peer-reported audit data,
18/// not authenticated transport provenance. Policy should use `source_network`
19/// for a usable routed origin and `source_mac` for the immediate data-link peer.
20/// `Debug` is redacted: address lengths, the sink, whether a principal is
21/// present and the notification count, never the notifications themselves.
22#[derive(Clone, PartialEq)]
23pub struct AuditNotificationAuthorizationContext {
24    /// Immediate data-link peer (normally a router for routed traffic).
25    pub source_mac: MacAddr,
26    /// Originating NPDU source when one was present.
27    pub source_network: Option<NpduAddress>,
28    /// Honest transport + origin provenance for this ingress; the source of
29    /// the principal, if any.
30    pub provenance: TransportProvenance,
31    /// Outer Confirmed-Request invoke identifier.
32    pub invoke_id: u8,
33    /// Explicitly configured local Audit Log sink.
34    pub audit_log_sink: ObjectIdentifier,
35    /// Decoded peer-reported notification list, preserved verbatim.
36    pub request: AuditNotificationRequest,
37}
38
39impl AuditNotificationAuthorizationContext {
40    /// The principal of the peer that originated the request: the verified
41    /// leaf certificate of a direct BACnet/SC connection, when the request
42    /// carries no routed source. `None` for other ingress, hub-relayed and
43    /// routed requests included (see [`crate::policy`]).
44    pub fn principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
45        crate::policy::originating_principal(self.provenance, self.source_network.as_ref())
46    }
47
48    /// The principal of the direct BACnet/SC peer that handed this request
49    /// over, a router included when the request was routed; `None` off a
50    /// verified direct SC connection. Trusting it trusts everything that
51    /// peer forwards.
52    pub fn forwarding_peer_principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
53        self.provenance.principal()
54    }
55}
56
57/// Transport provenance and decoded content supplied to
58/// [`ServerPolicy::authorize_unconfirmed_audit_notification`](crate::ServerPolicy::authorize_unconfirmed_audit_notification).
59///
60/// The source/target identities inside `request` are peer-reported audit data,
61/// not authenticated transport provenance. Policy should use `source_network`
62/// for a usable routed origin and `source_mac` for the immediate data-link peer.
63/// `Debug` is redacted: address lengths, the sink, whether a principal is
64/// present and the notification count, never the notifications themselves.
65#[derive(Clone, PartialEq)]
66pub struct UnconfirmedAuditNotificationAuthorizationContext {
67    /// Immediate data-link peer (normally a router for routed traffic).
68    pub source_mac: MacAddr,
69    /// Originating NPDU source when one was present.
70    pub source_network: Option<NpduAddress>,
71    /// Honest transport + origin provenance for this ingress; the source of
72    /// the principal, if any.
73    pub provenance: TransportProvenance,
74    /// Explicitly configured local Audit Log sink.
75    pub audit_log_sink: ObjectIdentifier,
76    /// Decoded peer-reported notification list, preserved verbatim.
77    pub request: AuditNotificationRequest,
78}
79
80impl UnconfirmedAuditNotificationAuthorizationContext {
81    /// The principal of the peer that originated the request: the verified
82    /// leaf certificate of a direct BACnet/SC connection, when the request
83    /// carries no routed source. `None` for other ingress, hub-relayed and
84    /// routed requests included (see [`crate::policy`]).
85    pub fn principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
86        crate::policy::originating_principal(self.provenance, self.source_network.as_ref())
87    }
88
89    /// The principal of the direct BACnet/SC peer that handed this request
90    /// over, a router included when the request was routed; `None` off a
91    /// verified direct SC connection. Trusting it trusts everything that
92    /// peer forwards.
93    pub fn forwarding_peer_principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
94        self.provenance.principal()
95    }
96}
97
98impl std::fmt::Debug for AuditNotificationAuthorizationContext {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        f.debug_struct("AuditNotificationAuthorizationContext")
101            .field("source_mac_len", &self.source_mac.len())
102            .field(
103                "source_network",
104                &self
105                    .source_network
106                    .as_ref()
107                    .map(|source| (source.network, source.mac_address.len())),
108            )
109            .field("provenance", &self.provenance)
110            .field("principal", &self.principal().is_some())
111            .field("invoke_id", &self.invoke_id)
112            .field("audit_log_sink", &self.audit_log_sink)
113            .field("notifications", &self.request.notifications.len())
114            .finish_non_exhaustive()
115    }
116}
117
118impl std::fmt::Debug for UnconfirmedAuditNotificationAuthorizationContext {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        f.debug_struct("UnconfirmedAuditNotificationAuthorizationContext")
121            .field("source_mac_len", &self.source_mac.len())
122            .field(
123                "source_network",
124                &self
125                    .source_network
126                    .as_ref()
127                    .map(|source| (source.network, source.mac_address.len())),
128            )
129            .field("provenance", &self.provenance)
130            .field("principal", &self.principal().is_some())
131            .field("audit_log_sink", &self.audit_log_sink)
132            .field("notifications", &self.request.notifications.len())
133            .finish_non_exhaustive()
134    }
135}