crowdstrike_cloudproto/services/
ts.rs

1//! High-level support for the TS event server
2
3mod acceptor;
4mod event;
5mod pkt_kind;
6mod socket;
7
8pub use acceptor::TsEventAcceptor;
9pub use event::{Event, EventId};
10pub use pkt_kind::TsPacketKind;
11pub use socket::TsEventSocket;
12
13use crate::services::{DEFAULT_BOOTID_HEX, DEFAULT_UNK0_HEX};
14
15/// Whether the server expects the client to keep its Agent ID or be assigned a new one
16#[repr(u8)]
17#[derive(Eq, PartialEq, Debug, Copy, Clone)]
18pub enum AgentIdStatus {
19    Unchanged = 0x1,
20    Changed = 0x2,
21}
22
23/// Connection information required to open a session with the TS server
24#[derive(Eq, PartialEq, Debug, Clone)]
25pub struct TsConnectInfo {
26    // The CID assigned to a Crowdstrike customer (same as the CCID without the last -N number)
27    // These are not random, there's a sort of checksum that must pass for a CID to be valid.
28    // For TS the CID needs to be not only valid, but belong to an active customer
29    pub cid: [u8; 16],
30    // Unknown, but has never changed and the AID returned by TS depends on it (can also be 0)
31    pub unk0: [u8; 16],
32    // Agent ID. Saved in "falconstore". New values can be assigned by the TS server on connection
33    pub aid: [u8; 16],
34    // Per-machine value (the stable /proc/sys/kernel/random/boot_id, or a timestamp if unavailable)
35    pub bootid: [u8; 16],
36    // The "PT" value from "falconstore". Can be left as zeroes.
37    pub pt: [u8; 8],
38}
39
40impl TsConnectInfo {
41    /// Connect using the provided Crowdstrike customer ID
42    /// The CID must belong to an active customer.
43    /// Unlike for the LSO server and falcon-sensor it's not enough to use a structurally valid but inactive CID.
44    /// Uses hardcoded default values for the other non-critical fields.
45    pub fn new_simple(cid: [u8; 16]) -> Self {
46        Self {
47            cid,
48            unk0: hex::decode(DEFAULT_UNK0_HEX).unwrap().try_into().unwrap(),
49            aid: [0; 16],
50            bootid: hex::decode(DEFAULT_BOOTID_HEX).unwrap().try_into().unwrap(),
51            pt: [0; 8],
52        }
53    }
54
55    pub fn new_custom(
56        cid: [u8; 16],
57        unk0: [u8; 16],
58        aid: [u8; 16],
59        bootid: [u8; 16],
60        pt: [u8; 8],
61    ) -> Self {
62        Self {
63            cid,
64            unk0,
65            aid,
66            bootid,
67            pt,
68        }
69    }
70}
71
72/// Response to a connection from the TS server
73#[derive(Eq, PartialEq, Debug, Clone)]
74pub struct TsConnectResponse {
75    // Whether the server expects us to keep our existing agent ID, or to update it
76    pub agent_id_status: AgentIdStatus,
77    // The agent ID assigned by the server
78    pub aid: [u8; 16],
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::framing::{CloudProtoError, CloudProtoSocket};
85    use futures_util::{SinkExt, StreamExt};
86    use tokio::spawn;
87
88    #[tokio::test]
89    async fn test_simple_client_server() -> Result<(), CloudProtoError> {
90        let (client, server) = tokio::io::duplex(16 * 1024);
91        let cid = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
92        let old_aid = [4, 4, 4, 4, 2, 2, 2, 2, 8, 8, 8, 8, 1, 1, 1, 1];
93        let new_aid = [9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0];
94
95        let server_task = spawn(async move {
96            let (server, info) = TsEventAcceptor::listen(CloudProtoSocket::new(server)).await?;
97            assert_eq!(info.cid, cid);
98            assert_eq!(info.aid, old_aid);
99            let mut sock = server
100                .accept(TsConnectResponse {
101                    agent_id_status: AgentIdStatus::Changed,
102                    aid: new_aid,
103                })
104                .await?;
105            let ev = sock.next().await.unwrap()?;
106            assert_eq!(ev.event_id, Some(EventId::AgentOnline));
107            sock.send(Event::new(
108                EventId::LfoDownloadFromManifestRecord,
109                vec![1, 2, 3],
110            ))
111            .await?;
112
113            Ok::<_, CloudProtoError>(sock) // Keep sock alive!
114        });
115
116        let mut client = TsEventSocket::connect(
117            CloudProtoSocket::new(client),
118            TsConnectInfo::new_custom(cid, [0; 16], old_aid, [0; 16], [0; 8]),
119        )
120        .await?;
121        client
122            .send(Event::new(EventId::AgentOnline, vec![]))
123            .await?;
124        let ev = client.next().await.unwrap()?;
125        assert_eq!(ev.event_id, Some(EventId::LfoDownloadFromManifestRecord));
126        assert_eq!(ev.data, &[1, 2, 3]);
127        server_task.await.expect("Server task join error!")?;
128        Ok(())
129    }
130}