GPLv2-Wireshark3.7.1-pgsql-decrypt-GSS.patch.txt
text/plain
Filename: GPLv2-Wireshark3.7.1-pgsql-decrypt-GSS.patch.txt
Type: text/plain
Part: 0
From 0cf31522223a6044edae42037e45aee0fc88352f Mon Sep 17 00:00:00 2001
From: Jacob Champion <jchampion@timescale.com>
Date: Wed, 14 Sep 2022 13:08:22 -0700
Subject: [PATCH] pgsql: decrypt GSS-encrypted channels and tokens if possible
License: GPLv2
Add dissection for a conversation that starts with a GSSAPI encryption
request, and pass those packets along to the gssapi dissector. This
allows parts of the conversation to be decrypted if Wireshark has been
set up with a KRB5 keytab.
Additionally, break apart the PGSQL_AUTH_GSSAPI_SSPI_DATA case into
separate GSSAPI and SSPI cases, and when we're using GSSAPI, dissect the
authentication tokens that are passed between the client and server.
This incidentally fixes an off-by-four bug in the
PGSQL_AUTH_TYPE_GSSAPI_SSPI_CONTINUE case; the offset was not being
updated correctly before.
The new code copies the dissection strategy of multipart, and adds a new
last_nongss_frame marker similar to the STARTTLS handling code's
last_nontls_frame.
---
epan/dissectors/packet-pgsql.c | 121 +++++++++++++++++++++++++++++++--
1 file changed, 116 insertions(+), 5 deletions(-)
diff --git a/epan/dissectors/packet-pgsql.c b/epan/dissectors/packet-pgsql.c
index c935eec1f8..7efe1c003f 100644
--- a/epan/dissectors/packet-pgsql.c
+++ b/epan/dissectors/packet-pgsql.c
@@ -14,6 +14,8 @@
#include <epan/packet.h>
+#include "packet-dcerpc.h"
+#include "packet-gssapi.h"
#include "packet-tls-utils.h"
#include "packet-tcp.h"
@@ -22,6 +24,7 @@ void proto_reg_handoff_pgsql(void);
static dissector_handle_t pgsql_handle;
static dissector_handle_t tls_handle;
+static dissector_handle_t gssapi_handle;
static int proto_pgsql = -1;
static int hf_frontend = -1;
@@ -80,10 +83,13 @@ static int hf_constraint_name = -1;
static int hf_file = -1;
static int hf_line = -1;
static int hf_routine = -1;
+static int hf_gssapi_length = -1;
static gint ett_pgsql = -1;
static gint ett_values = -1;
+static expert_field ei_gssapi_decryption_not_possible = EI_INIT;
+
#define PGSQL_PORT 5432
static gboolean pgsql_desegment = TRUE;
static gboolean first_message = TRUE;
@@ -92,11 +98,14 @@ typedef enum {
PGSQL_AUTH_STATE_NONE, /* No authentication seen or used */
PGSQL_AUTH_SASL_REQUESTED, /* Server sends SASL auth request with supported SASL mechanisms*/
PGSQL_AUTH_SASL_CONTINUE, /* Server and/or client send further SASL challange-response messages */
- PGSQL_AUTH_GSSAPI_SSPI_DATA, /* GSSAPI/SSPI in use */
+ PGSQL_AUTH_GSSAPI, /* GSSAPI in use */
+ PGSQL_AUTH_SSPI, /* SSPI in use */
} pgsql_auth_state_t;
typedef struct pgsql_conn_data {
gboolean ssl_requested;
+ gboolean gss_requested;
+ guint32 last_nongss_frame;
pgsql_auth_state_t auth_state; /* Current authentication state */
} pgsql_conn_data_t;
@@ -189,6 +198,22 @@ static const value_string format_vals[] = {
{ 0, NULL }
};
+static void
+dissect_gssapi_data(tvbuff_t *tvb, gint offset, guint len, packet_info *pinfo,
+ proto_tree *tree, gssapi_encrypt_info_t *encrypt)
+{
+ tvbuff_t *gssapi_tvb;
+ guint8 *data;
+
+ DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));
+
+ data = (guint8 *)tvb_memdup(pinfo->pool, tvb, offset, len);
+ gssapi_tvb = tvb_new_child_real_data(tvb, data, len, len);
+
+ add_new_data_source(pinfo, gssapi_tvb, "GSSAPI Data");
+ call_dissector_with_data(gssapi_handle, gssapi_tvb, pinfo, tree, encrypt);
+}
+
static void dissect_pgsql_fe_msg(guchar type, guint length, tvbuff_t *tvb,
gint n, proto_tree *tree, packet_info *pinfo,
pgsql_conn_data_t *conv_data)
@@ -220,7 +245,12 @@ static void dissect_pgsql_fe_msg(guchar type, guint length, tvbuff_t *tvb,
proto_tree_add_item(tree, hf_sasl_auth_data, tvb, n, length-4, ENC_NA);
break;
- case PGSQL_AUTH_GSSAPI_SSPI_DATA:
+ case PGSQL_AUTH_GSSAPI:
+ proto_tree_add_item(tree, hf_gssapi_sspi_data, tvb, n, length-4, ENC_NA);
+ dissect_gssapi_data(tvb, n, length-4, pinfo, tree, NULL);
+ break;
+
+ case PGSQL_AUTH_SSPI:
proto_tree_add_item(tree, hf_gssapi_sspi_data, tvb, n, length-4, ENC_NA);
break;
@@ -356,6 +386,12 @@ static void dissect_pgsql_fe_msg(guchar type, guint length, tvbuff_t *tvb,
conv_data->ssl_requested = TRUE;
break;
+ /* GSS request */
+ case 80877104:
+ /* Next reply will be a single byte. */
+ conv_data->gss_requested = TRUE;
+ break;
+
/* Cancellation request */
case 80877102:
proto_tree_add_item(tree, hf_pid, tvb, n, 4, ENC_BIG_ENDIAN);
@@ -430,9 +466,17 @@ static void dissect_pgsql_be_msg(guchar type, guint length, tvbuff_t *tvb,
siz = (auth_type == PGSQL_AUTH_TYPE_CRYPT ? 2 : 4);
proto_tree_add_item(tree, hf_salt, tvb, n, siz, ENC_NA);
break;
+ case PGSQL_AUTH_TYPE_GSSAPI:
+ conv_data->auth_state = PGSQL_AUTH_GSSAPI;
+ break;
+ case PGSQL_AUTH_TYPE_SSPI:
+ conv_data->auth_state = PGSQL_AUTH_SSPI;
+ break;
case PGSQL_AUTH_TYPE_GSSAPI_SSPI_CONTINUE:
- conv_data->auth_state = PGSQL_AUTH_GSSAPI_SSPI_DATA;
+ n += 4;
proto_tree_add_item(tree, hf_gssapi_sspi_data, tvb, n, length-8, ENC_NA);
+ if (conv_data->auth_state == PGSQL_AUTH_GSSAPI)
+ dissect_gssapi_data(tvb, n, length-8, pinfo, tree, NULL);
break;
case PGSQL_AUTH_TYPE_SASL:
conv_data->auth_state = PGSQL_AUTH_SASL_REQUESTED;
@@ -665,6 +709,8 @@ dissect_pgsql_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
if (!conn_data) {
conn_data = wmem_new(wmem_file_scope(), pgsql_conn_data_t);
conn_data->ssl_requested = FALSE;
+ conn_data->gss_requested = FALSE;
+ conn_data->last_nongss_frame = G_MAXUINT32;
conn_data->auth_state = PGSQL_AUTH_STATE_NONE;
conversation_add_proto_data(conversation, proto_pgsql, conn_data);
}
@@ -703,7 +749,8 @@ dissect_pgsql_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
case PGSQL_AUTH_SASL_CONTINUE:
typestr = "SASLResponse message";
break;
- case PGSQL_AUTH_GSSAPI_SSPI_DATA:
+ case PGSQL_AUTH_GSSAPI: /* fallthrough */
+ case PGSQL_AUTH_SSPI:
typestr = "GSSResponse message";
break;
default:
@@ -746,6 +793,18 @@ dissect_pgsql_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
return tvb_captured_length(tvb);
}
+static void
+dissect_gssenc_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gssapi_encrypt_info_t *encrypt)
+{
+ gint offset = 0, len;
+
+ proto_tree_add_item(tree, hf_gssapi_length, tvb, offset, 4, ENC_BIG_ENDIAN);
+ offset += 4;
+ len = tvb_reported_length_remaining(tvb, offset);
+
+ dissect_gssapi_data(tvb, offset, len, pinfo, tree, encrypt);
+}
+
/* This function is called once per TCP packet. It sets COL_PROTOCOL and
* identifies FE/BE messages by adding a ">" or "<" to COL_INFO. Then it
* arranges for each message to be dissected individually. */
@@ -782,6 +841,43 @@ dissect_pgsql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
return tvb_captured_length(tvb);
}
+ if (conn_data && conn_data->gss_requested) {
+ /* Response to GSS request. */
+ switch (tvb_get_guint8(tvb, 0)) {
+ case 'G': /* Willing to perform GSS encryption */
+ /* Next packet will start using GSS encryption. */
+ conn_data->last_nongss_frame = pinfo->num;
+ break;
+ case 'N': /* Unwilling to perform GSS encryption */
+ default: /* ErrorMessage when server does not support SSL. */
+ /* TODO: maybe add expert info here? */
+ break;
+ }
+ conn_data->gss_requested = FALSE;
+ return tvb_captured_length(tvb);
+ }
+
+ if (conn_data && (pinfo->num > conn_data->last_nongss_frame)) {
+ tvbuff_t *subtvb;
+ gssapi_encrypt_info_t encrypt;
+
+ memset(&encrypt, 0, sizeof(encrypt));
+ encrypt.decrypt_gssapi_tvb = DECRYPT_GSSAPI_NORMAL;
+
+ dissect_gssenc_msg(tvb, pinfo, tree, &encrypt);
+
+ if (encrypt.gssapi_decrypted_tvb){
+ subtvb = encrypt.gssapi_decrypted_tvb;
+ tcp_dissect_pdus(subtvb, pinfo, tree, pgsql_desegment, 5,
+ pgsql_length, dissect_pgsql_msg, data);
+ } else if (encrypt.gssapi_encrypted_tvb) {
+ subtvb = encrypt.gssapi_encrypted_tvb;
+ proto_tree_add_expert(tree, pinfo, &ei_gssapi_decryption_not_possible, subtvb, 0, -1);
+ }
+
+ return tvb_captured_length(tvb);
+ }
+
tcp_dissect_pdus(tvb, pinfo, tree, pgsql_desegment, 5,
pgsql_length, dissect_pgsql_msg, data);
return tvb_captured_length(tvb);
@@ -1021,7 +1117,12 @@ proto_register_pgsql(void)
{ &hf_routine,
{ "Routine", "pgsql.routine", FT_STRINGZ, BASE_NONE, NULL, 0,
"The routine that reported an error.", HFILL }
- }
+ },
+ { &hf_gssapi_length,
+ { "Length of GSSAPI encrypted data", "pgsql.gss.length", FT_UINT32, BASE_DEC, NULL, 0,
+ "The length of the GSSAPI encrypted blob.",
+ HFILL }
+ },
};
static gint *ett[] = {
@@ -1029,9 +1130,18 @@ proto_register_pgsql(void)
&ett_values
};
+ expert_module_t* expert_pgsql;
+
+ static ei_register_info ei[] = {
+ { &ei_gssapi_decryption_not_possible, { "pgsql.gss.decryption_not_possible", PI_UNDECODED, PI_WARN, "The PGSQL dissector could not decrypt the message.", EXPFILL }},
+ };
+
proto_pgsql = proto_register_protocol("PostgreSQL", "PGSQL", "pgsql");
proto_register_field_array(proto_pgsql, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+
+ expert_pgsql = expert_register_protocol(proto_pgsql);
+ expert_register_field_array(expert_pgsql, ei, array_length(ei));
}
void
@@ -1042,6 +1152,7 @@ proto_reg_handoff_pgsql(void)
dissector_add_uint_with_preference("tcp.port", PGSQL_PORT, pgsql_handle);
tls_handle = find_dissector_add_dependency("tls", proto_pgsql);
+ gssapi_handle = find_dissector_add_dependency("gssapi", proto_pgsql);
}
/*
--
2.25.1