to be exchanged as subprotocols.
* "message": frontend responses which do not have meaningful payloads are logged
like this, where the field value is the message type
+* ``"message": "cancel_request"``: sent after a query, when the frontend
+ attempts to cancel said query. This message is sent over a different port,
+ thus bring shown as a different flow. It has no direct answer from the
+ backend, but if successful will lead to an ``ErrorResponse`` in the
+ transaction where the query was sent.
There are several different authentication messages possible, based on selected
authentication method. (e.g. the SASL authentication will have a set of
}
}
+``AuthenticationOk``: a response indicating that the connection was successfully
+established.::
+
+ {
+ "pgsql": {
+ "tx_id": 3,
+ "response": {
+ "message": "authentication_ok",
+ "parameter_status": [
+ {
+ "application_name": "psql"
+ },
+ {
+ "client_encoding": "UTF8"
+ },
+ {
+ "date_style": "ISO, MDY"
+ },
+ {
+ "integer_datetimes": "on"
+ },
+ {
+ "interval_style": "postgres"
+ },
+ {
+ "is_superuser": "on"
+ },
+ {
+ "server_encoding": "UTF8"
+ },
+ {
+ "server_version": "13.6 (Debian 13.6-1.pgdg110+1)"
+ },
+ {
+ "session_authorization": "rules"
+ },
+ {
+ "standard_conforming_strings": "on"
+ },
+ {
+ "time_zone": "Etc/UTC"
+ }
+ ],
+ "process_id": 28954,
+ "secret_key": 889887985
+ }
+ }
+ }
+
+.. note::
+ In Suricata, the ``AuthenticationOk`` message is also where the backend's
+ ``process_id`` and ``secret_key`` are logged. These must be sent by the
+ frontend when it issues a ``CancelRequest`` message (seen below).
+
+A ``CancelRequest`` message::
+
+ {
+ "timestamp": "2023-12-07T15:46:56.971150+0000",
+ "flow_id": 775771889500133,
+ "event_type": "pgsql",
+ "src_ip": "100.88.2.140",
+ "src_port": 39706,
+ "dest_ip": "100.96.199.113",
+ "dest_port": 5432,
+ "proto": "TCP",
+ "pkt_src": "stream (flow timeout)",
+ "pgsql": {
+ "tx_id": 1,
+ "request": {
+ "message": "cancel_request",
+ "process_id": 28954,
+ "secret_key": 889887985
+ }
+ }
+ }
+
+.. note::
+ As the ``CancelRequest`` message is sent over a new connection, the way to
+ correlate it with the proper frontend/flow from which it originates is by
+ querying on ``process_id`` and ``secret_key`` seen in the
+ ``AuthenticationOk`` event.
+
+References:
+ * `PostgreSQL protocol - Canceling Requests in Progress`_
+ * `PostgreSQL message format - BackendKeyData`_
+
+.. _PostgreSQL protocol - Canceling Requests in Progress: https://www.postgresql
+ .org/docs/current/protocol-flow.html#PROTOCOL-FLOW-CANCELING-REQUESTS
+.. _PostgreSQL message format - BackendKeyData: https://www.postgresql.org/docs
+ /current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-BACKENDKEYDATA
+
Event type: IKE
---------------
pub const PGSQL_LENGTH_FIELD: u32 = 4;
pub const PGSQL_DUMMY_PROTO_MAJOR: u16 = 1234; // 0x04d2
+pub const PGSQL_DUMMY_PROTO_CANCEL_REQUEST: u16 = 5678; // 0x162e
pub const PGSQL_DUMMY_PROTO_MINOR_SSL: u16 = 5679; //0x162f
pub const _PGSQL_DUMMY_PROTO_MINOR_GSSAPI: u16 = 5680; // 0x1630
pub length: u32,
}
+#[derive(Debug, PartialEq, Eq)]
+pub struct CancelRequestMessage {
+ pub pid: u32,
+ pub backend_key: u32,
+}
+
#[derive(Debug, PartialEq, Eq)]
pub enum PgsqlFEMessage {
SSLRequest(DummyStartupPacket),
SASLInitialResponse(SASLInitialResponsePacket),
SASLResponse(RegularPacket),
SimpleQuery(RegularPacket),
+ CancelRequest(CancelRequestMessage),
Terminate(TerminationMessage),
UnknownMessageType(RegularPacket),
}
PgsqlFEMessage::SASLInitialResponse(_) => "sasl_initial_response",
PgsqlFEMessage::SASLResponse(_) => "sasl_response",
PgsqlFEMessage::SimpleQuery(_) => "simple_query",
+ PgsqlFEMessage::CancelRequest(_) => "cancel_request",
PgsqlFEMessage::Terminate(_) => "termination_message",
PgsqlFEMessage::UnknownMessageType(_) => "unknown_message_type",
}
},
PGSQL_DUMMY_PROTO_MAJOR => {
let (b, proto_major) = be_u16(b)?;
- let (b, proto_minor) = all_consuming(be_u16)(b)?;
- let _message = match proto_minor {
- PGSQL_DUMMY_PROTO_MINOR_SSL => (len, proto_major, proto_minor),
+ let (b, proto_minor) = be_u16(b)?;
+ let (b, message) = match proto_minor {
+ PGSQL_DUMMY_PROTO_CANCEL_REQUEST => {
+ parse_cancel_request(b)?
+ },
+ PGSQL_DUMMY_PROTO_MINOR_SSL => (b, PgsqlFEMessage::SSLRequest(DummyStartupPacket{
+ length: len,
+ proto_major,
+ proto_minor
+ })),
_ => return Err(Err::Error(make_error(b, ErrorKind::Switch))),
};
- (b, PgsqlFEMessage::SSLRequest(DummyStartupPacket{
- length: len,
- proto_major,
- proto_minor}))
+ (b, message)
}
_ => return Err(Err::Error(make_error(b, ErrorKind::Switch))),
};
})))
}
+fn parse_cancel_request(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> {
+ let (i, pid) = be_u32(i)?;
+ let (i, backend_key) = be_u32(i)?;
+ Ok((i, PgsqlFEMessage::CancelRequest(CancelRequestMessage {
+ pid,
+ backend_key,
+ })))
+}
+
fn parse_terminate_message(i: &[u8]) -> IResult<&[u8], PgsqlFEMessage> {
let (i, identifier) = verify(be_u8, |&x| x == b'X')(i)?;
let (i, length) = parse_length(i)?;
let result = parse_request(&buf[0..3]);
assert!(result.is_err());
- // TODO add other messages
}
+ #[test]
+ fn test_cancel_request_message() {
+ // A cancel request message
+ let buf: &[u8] = &[
+ 0x00, 0x00, 0x00, 0x10, // length: 16 (fixed)
+ 0x04, 0xd2, 0x16, 0x2e, // 1234.5678 - identifies a cancel request
+ 0x00, 0x00, 0x76, 0x31, // PID: 30257
+ 0x23, 0x84, 0xf7, 0x2d]; // Backend key: 595916589
+ let result = parse_cancel_request(buf);
+ assert!(result.is_ok());
+
+ let result = parse_cancel_request(&buf[0..3]);
+ assert!(result.is_err());
+
+ let result = pgsql_parse_startup_packet(buf);
+ assert!(result.is_ok());
+
+ let fail_result = pgsql_parse_startup_packet(&buf[0..3]);
+ assert!(fail_result.is_err());
+
+ let result = parse_request(buf);
+ assert!(result.is_ok());
+
+ let fail_result = parse_request(&buf[0..3]);
+ assert!(fail_result.is_err());
+ }
+
+
+
#[test]
fn test_parse_error_response_code() {
let buf: &[u8] = &[0x43, 0x32, 0x38, 0x30, 0x30, 0x30, 0x00];