aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-smtp.c
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2012-06-11 07:10:18 +0000
committerAnders Broman <anders.broman@ericsson.com>2012-06-11 07:10:18 +0000
commit8456dcbcebf060683bf14bf5a6e00e6401f426b1 (patch)
treef81cb7f441fec4e4cb5ceb4cfee5b7d16b303026 /epan/dissectors/packet-smtp.c
parent34cb6f96d3bb52c0345d72b37c34ffd0da07a10e (diff)
From Michael Mann:
Implemented RFC 4954 within the SMTP dissector. On principle, the decryption is disabled by default (making this feature not obviously present). However I don't think there will ever be enough data that the performance would be an issue if the default was changed. https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=7349 svn path=/trunk/; revision=43197
Diffstat (limited to 'epan/dissectors/packet-smtp.c')
-rw-r--r--epan/dissectors/packet-smtp.c343
1 files changed, 228 insertions, 115 deletions
diff --git a/epan/dissectors/packet-smtp.c b/epan/dissectors/packet-smtp.c
index e613322420..70b79ed312 100644
--- a/epan/dissectors/packet-smtp.c
+++ b/epan/dissectors/packet-smtp.c
@@ -5,6 +5,9 @@
*
* Copyright (c) 2000 by Richard Sharpe <rsharpe@ns.aus.com>
*
+ * Added RFC 4954 SMTP Authentication
+ * Michael Mann * Copyright 2012
+ *
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1999 Gerald Combs
@@ -39,6 +42,7 @@
#include <epan/strutil.h>
#include <epan/emem.h>
#include <epan/reassemble.h>
+#include <epan/base64.h>
#include <epan/dissectors/packet-ssl.h>
/* RFC 2821 */
@@ -59,6 +63,8 @@ static int hf_smtp_req_parameter = -1;
static int hf_smtp_response = -1;
static int hf_smtp_rsp_code = -1;
static int hf_smtp_rsp_parameter = -1;
+static int hf_smtp_username = -1;
+static int hf_smtp_password = -1;
static int hf_smtp_data_fragments = -1;
static int hf_smtp_data_fragment = -1;
@@ -77,6 +83,7 @@ static int ett_smtp_cmdresp = -1;
static gint ett_smtp_data_fragment = -1;
static gint ett_smtp_data_fragments = -1;
+static gboolean stmp_decryption_enabled = FALSE;
/* desegmentation of SMTP command and response lines */
static gboolean smtp_desegment = TRUE;
static gboolean smtp_data_desegment = TRUE;
@@ -131,8 +138,25 @@ typedef enum {
SMTP_STATE_AWAITING_STARTTLS_RESPONSE /* sent STARTTLS, awaiting response */
} smtp_state_t;
+typedef enum {
+ SMTP_AUTH_STATE_NONE, /* No authentication seen or used */
+ SMTP_AUTH_STATE_START, /* Authentication started, waiting for username */
+ SMTP_AUTH_STATE_USERNAME_REQ, /* Received username request from server */
+ SMTP_AUTH_STATE_USERNAME_RSP, /* Received username response from client */
+ SMTP_AUTH_STATE_PASSWORD_REQ, /* Received password request from server */
+ SMTP_AUTH_STATE_PASSWORD_RSP, /* Received password request from server */
+ SMTP_AUTH_STATE_SUCCESS, /* Password received, authentication successful, start decoding */
+ SMTP_AUTH_STATE_FAILED, /* authentication failed, no decoding */
+} smtp_auth_state_t;
+
struct smtp_session_state {
smtp_state_t smtp_state; /* Current state */
+ smtp_auth_state_t auth_state; /* Current authentication state */
+ /* Values that need to be saved because state machine can't be used during tree dissection */
+ guint32 first_auth_frame; /* First frame involving authentication. */
+ guint32 username_frame; /* Frame containing client username */
+ guint32 password_frame; /* Frame containing client password */
+ guint32 last_auth_frame; /* Last frame involving authentication. */
gboolean crlf_seen; /* Have we seen a CRLF on the end of a packet */
gboolean data_seen; /* Have we seen a DATA command yet */
guint32 msg_read_len; /* Length of BDAT message read so far */
@@ -166,19 +190,27 @@ static const value_string response_codes_vs[] = {
{ 214, "Help message" },
{ 220, "<domain> Service ready" },
{ 221, "<domain> Service closing transmission channel" },
+ { 235, "Authentication successful" },
{ 250, "Requested mail action okay, completed" },
{ 251, "User not local; will forward to <forward-path>" },
{ 252, "Cannot VRFY user, but will accept message and attempt delivery" },
+ { 334, "AUTH input" },
{ 354, "Start mail input; end with <CRLF>.<CRLF>" },
{ 421, "<domain> Service not available, closing transmission channel" },
+ { 432, "A password transition is needed" },
{ 450, "Requested mail action not taken: mailbox unavailable" },
{ 451, "Requested action aborted: local error in processing" },
{ 452, "Requested action not taken: insufficient system storage" },
+ { 454, "Temporary authenticaion failed" },
{ 500, "Syntax error, command unrecognized" },
{ 501, "Syntax error in parameters or arguments" },
{ 502, "Command not implemented" },
{ 503, "Bad sequence of commands" },
{ 504, "Command parameter not implemented" },
+ { 530, "Authentication required" },
+ { 534, "Authentication mechanism is too weak" },
+ { 535, "Authentication credentials invalid" },
+ { 538, "Encryption required for requested authentication mechanism" },
{ 550, "Requested action not taken: mailbox unavailable" },
{ 551, "User not local; please try <forward-path>" },
{ 552, "Requested mail action aborted: exceeded storage allocation" },
@@ -269,6 +301,8 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
int cmdlen;
fragment_data *frag_msg = NULL;
tvbuff_t *next_tvb;
+ guint8 *decrypt = NULL;
+ guint8 line_code[3];
/* As there is no guarantee that we will only see frames in the
* the SMTP conversation once, and that we will see them in
@@ -304,6 +338,9 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
*/
session_state = se_alloc(sizeof(struct smtp_session_state));
session_state->smtp_state = SMTP_STATE_READING_CMDS;
+ session_state->auth_state = SMTP_AUTH_STATE_NONE;
+ session_state->first_auth_frame = 0;
+ session_state->last_auth_frame = 0;
session_state->crlf_seen = FALSE;
session_state->data_seen = FALSE;
session_state->msg_read_len = 0;
@@ -392,7 +429,6 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
next_offset = loffset + linelen;
}
}
- line = tvb_get_ptr(tvb, loffset, linelen);
/*
* Check whether or not this packet is an end of message packet
@@ -476,6 +512,19 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
* for a space or the end of the line to see where
* the putative command ends.
*/
+ if ((session_state->auth_state != SMTP_AUTH_STATE_NONE) &&
+ (pinfo->fd->num >= session_state->first_auth_frame) &&
+ ((session_state->last_auth_frame == 0) || (pinfo->fd->num <= session_state->last_auth_frame))) {
+ decrypt = tvb_get_ephemeral_string(tvb, loffset, linelen);
+ if ((stmp_decryption_enabled) && (epan_base64_decode(decrypt) > 0)) {
+ line = decrypt;
+ } else {
+ line = tvb_get_ptr(tvb, loffset, linelen);
+ }
+ } else {
+ line = tvb_get_ptr(tvb, loffset, linelen);
+ }
+
linep = line;
lineend = line + linelen;
while (linep < lineend && *linep != ' ')
@@ -528,6 +577,16 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
} else {
session_state->msg_last = FALSE;
}
+ } else if (g_ascii_strncasecmp(line, "AUTH", 4) == 0) {
+ /*
+ * DATA command.
+ * This is a command, but everything that comes after it,
+ * until an EOM, is data.
+ */
+ spd_frame_data->pdu_type = SMTP_PDU_CMD;
+ session_state->smtp_state = SMTP_STATE_READING_CMDS;
+ session_state->auth_state = SMTP_AUTH_STATE_START;
+ session_state->first_auth_frame = pinfo->fd->num;
} else if (g_ascii_strncasecmp(line, "STARTTLS", 8) == 0) {
/*
* STARTTLS command.
@@ -542,7 +601,15 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
*/
spd_frame_data->pdu_type = SMTP_PDU_CMD;
}
- } else {
+ } else if (session_state->auth_state == SMTP_AUTH_STATE_USERNAME_REQ) {
+ session_state->auth_state = SMTP_AUTH_STATE_USERNAME_RSP;
+ session_state->username_frame = pinfo->fd->num;
+ } else if (session_state->auth_state == SMTP_AUTH_STATE_PASSWORD_REQ) {
+ session_state->auth_state = SMTP_AUTH_STATE_PASSWORD_RSP;
+ session_state->password_frame = pinfo->fd->num;
+ }
+ else {
+
/*
* Assume it's message data.
*/
@@ -565,72 +632,7 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
*/
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMTP");
-
- if (check_col(pinfo->cinfo, COL_INFO)) { /* Add the appropriate type here */
- col_clear(pinfo->cinfo, COL_INFO);
-
- /*
- * If it is a request, we have to look things up, otherwise, just
- * display the right things
- */
-
- if (request) {
- /* We must have frame_data here ... */
- switch (spd_frame_data->pdu_type) {
- case SMTP_PDU_MESSAGE:
-
- length_remaining = tvb_length_remaining(tvb, offset);
- col_set_str(pinfo->cinfo, COL_INFO, smtp_data_desegment ? "C: DATA fragment" : "C: Message Body");
- col_append_fstr(pinfo->cinfo, COL_INFO, ", %d byte%s", length_remaining,
- plurality (length_remaining, "", "s"));
- break;
-
- case SMTP_PDU_EOM:
- col_set_str(pinfo->cinfo, COL_INFO, "C: .");
- break;
-
- case SMTP_PDU_CMD:
- loffset = offset;
- while (tvb_offset_exists(tvb, loffset)) {
- /*
- * Find the end of the line.
- */
- linelen = tvb_find_line_end(tvb, loffset, -1, &next_offset, FALSE);
- line = tvb_get_ptr(tvb, loffset, linelen);
-
- if(loffset == offset)
- col_append_fstr(pinfo->cinfo, COL_INFO, "C: %s",
- format_text(line, linelen));
- else {
- col_append_fstr(pinfo->cinfo, COL_INFO, " | %s",
- format_text(line, linelen));
- }
-
- loffset = next_offset;
- }
- break;
- }
- } else {
- loffset = offset;
- while (tvb_offset_exists(tvb, loffset)) {
- /*
- * Find the end of the line.
- */
- linelen = tvb_find_line_end(tvb, loffset, -1, &next_offset, FALSE);
- line = tvb_get_ptr(tvb, loffset, linelen);
-
- if (loffset == offset)
- col_append_fstr(pinfo->cinfo, COL_INFO, "S: %s",
- format_text(line, linelen));
- else {
- col_append_fstr(pinfo->cinfo, COL_INFO, " | %s",
- format_text(line, linelen));
- }
-
- loffset = next_offset;
- }
- }
- }
+ col_clear(pinfo->cinfo, COL_INFO);
if (tree) { /* Build the tree info ... */
ti = proto_tree_add_item(tree, proto_smtp, tvb, offset, -1, ENC_NA);
@@ -653,6 +655,12 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
switch (spd_frame_data->pdu_type) {
case SMTP_PDU_MESSAGE:
+ /* Column Info */
+ length_remaining = tvb_length_remaining(tvb, offset);
+ col_set_str(pinfo->cinfo, COL_INFO, smtp_data_desegment ? "C: DATA fragment" : "C: Message Body");
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", %d byte%s", length_remaining,
+ plurality (length_remaining, "", "s"));
+
if (smtp_data_desegment) {
frag_msg = fragment_add_seq_next(tvb, 0, pinfo, spd_frame_data->conversation_id,
smtp_data_segment_table, smtp_data_reassembled_table,
@@ -675,6 +683,8 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
* DATA command this terminates before sending another
* request, but we should probably handle it.
*/
+ col_set_str(pinfo->cinfo, COL_INFO, "C: .");
+
proto_tree_add_text(smtp_tree, tvb, offset, linelen, "C: .");
if (smtp_data_desegment) {
@@ -707,34 +717,72 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
*/
linelen = tvb_find_line_end(tvb, loffset, -1, &next_offset, FALSE);
- if (linelen >= 4)
- cmdlen = 4;
+ /* Column Info */
+ if(loffset == offset)
+ col_append_str(pinfo->cinfo, COL_INFO, "C: ");
else
- cmdlen = linelen;
+ col_append_str(pinfo->cinfo, COL_INFO, " | ");
+
hidden_item = proto_tree_add_boolean(smtp_tree, hf_smtp_req, tvb,
0, 0, TRUE);
PROTO_ITEM_SET_HIDDEN(hidden_item);
- /*
- * Put the command line into the protocol tree.
- */
- ti = proto_tree_add_item(smtp_tree, hf_smtp_command_line, tvb,
+ if (session_state->username_frame == pinfo->fd->num) {
+ if (decrypt == NULL) {
+ /* This line wasn't already decrypted through the state machine */
+ decrypt = tvb_get_ephemeral_string(tvb, loffset, linelen);
+ if (stmp_decryption_enabled) {
+ if (epan_base64_decode(decrypt) == 0) {
+ /* Go back to the original string */
+ decrypt = tvb_get_ephemeral_string(tvb, loffset, linelen);
+ }
+ }
+ }
+ proto_tree_add_string(smtp_tree, hf_smtp_username, tvb,
+ loffset, linelen, decrypt);
+ col_append_str(pinfo->cinfo, COL_INFO, decrypt);
+ } else if (session_state->password_frame == pinfo->fd->num) {
+ if (decrypt == NULL) {
+ /* This line wasn't already decrypted through the state machine */
+ decrypt = tvb_get_ephemeral_string(tvb, loffset, linelen);
+ if (stmp_decryption_enabled) {
+ if (epan_base64_decode(decrypt) == 0) {
+ /* Go back to the original string */
+ decrypt = tvb_get_ephemeral_string(tvb, loffset, linelen);
+ }
+ }
+ }
+ proto_tree_add_string(smtp_tree, hf_smtp_password, tvb,
+ loffset, linelen, decrypt);
+ col_append_str(pinfo->cinfo, COL_INFO, decrypt);
+ } else {
+ col_append_str(pinfo->cinfo, COL_INFO, tvb_get_ephemeral_string(tvb, loffset, linelen));
+
+ if (linelen >= 4)
+ cmdlen = 4;
+ else
+ cmdlen = linelen;
+
+ /*
+ * Put the command line into the protocol tree.
+ */
+ ti = proto_tree_add_item(smtp_tree, hf_smtp_command_line, tvb,
loffset, next_offset - loffset, ENC_ASCII|ENC_NA);
- cmdresp_tree = proto_item_add_subtree(ti, ett_smtp_cmdresp);
+ cmdresp_tree = proto_item_add_subtree(ti, ett_smtp_cmdresp);
- proto_tree_add_item(cmdresp_tree, hf_smtp_req_command, tvb,
+ proto_tree_add_item(cmdresp_tree, hf_smtp_req_command, tvb,
loffset, cmdlen, ENC_ASCII|ENC_NA);
- if (linelen > 5) {
- proto_tree_add_item(cmdresp_tree, hf_smtp_req_parameter, tvb,
+ if (linelen > 5) {
+ proto_tree_add_item(cmdresp_tree, hf_smtp_req_parameter, tvb,
loffset + 5, linelen - 5, ENC_ASCII|ENC_NA);
- }
+ }
- if (smtp_data_desegment && !spd_frame_data->more_frags) {
- /* terminate the desegmentation */
- frag_msg = fragment_end_seq_next (pinfo, spd_frame_data->conversation_id, smtp_data_segment_table,
- smtp_data_reassembled_table);
+ if (smtp_data_desegment && !spd_frame_data->more_frags) {
+ /* terminate the desegmentation */
+ frag_msg = fragment_end_seq_next (pinfo, spd_frame_data->conversation_id, smtp_data_segment_table,
+ smtp_data_reassembled_table);
+ }
}
-
/*
* Step past this line.
*/
@@ -773,12 +821,18 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
PROTO_ITEM_SET_HIDDEN(hidden_item);
}
+ loffset = offset;
while (tvb_offset_exists(tvb, offset)) {
/*
* Find the end of the line.
*/
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
+ if (loffset == offset)
+ col_append_str(pinfo->cinfo, COL_INFO, "S: ");
+ else
+ col_append_str(pinfo->cinfo, COL_INFO, " | ");
+
if (tree) {
/*
* Put it into the protocol tree.
@@ -789,41 +843,88 @@ dissect_smtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
} else
cmdresp_tree = NULL;
- line = tvb_get_ptr(tvb, offset, linelen);
- if (linelen >= 3 && isdigit(line[0]) && isdigit(line[1])
- && isdigit(line[2])) {
- /*
- * We have a 3-digit response code.
- */
- code = (line[0] - '0')*100 + (line[1] - '0')*10 + (line[2] - '0');
+ if (linelen >= 3) {
+ line_code[0] = tvb_get_guint8(tvb, offset);
+ line_code[1] = tvb_get_guint8(tvb, offset+1);
+ line_code[2] = tvb_get_guint8(tvb, offset+2);
+ if (isdigit(line_code[0]) && isdigit(line_code[1])
+ && isdigit(line_code[2])) {
+ /*
+ * We have a 3-digit response code.
+ */
+ code = (line_code[0] - '0')*100 + (line_code[1] - '0')*10 + (line_code[2] - '0');
- /*
- * If we're awaiting the response to a STARTTLS code, this
- * is it - if it's 220, all subsequent traffic will
- * be TLS, otherwise we're back to boring old SMTP.
- */
- if (session_state->smtp_state == SMTP_STATE_AWAITING_STARTTLS_RESPONSE) {
- if (code == 220) {
- /* This is the last non-TLS frame. */
- session_state->last_nontls_frame = pinfo->fd->num;
- }
- session_state->smtp_state = SMTP_STATE_READING_CMDS;
- }
+ /*
+ * If we're awaiting the response to a STARTTLS code, this
+ * is it - if it's 220, all subsequent traffic will
+ * be TLS, otherwise we're back to boring old SMTP.
+ */
+ if (session_state->smtp_state == SMTP_STATE_AWAITING_STARTTLS_RESPONSE) {
+ if (code == 220) {
+ /* This is the last non-TLS frame. */
+ session_state->last_nontls_frame = pinfo->fd->num;
+ }
+ session_state->smtp_state = SMTP_STATE_READING_CMDS;
+ }
- if (tree) {
- /*
- * Put the response code and parameters into the protocol tree.
- */
- proto_tree_add_uint(cmdresp_tree, hf_smtp_rsp_code, tvb, offset, 3,
- code);
+ if (code == 334) {
+ switch(session_state->auth_state)
+ {
+ case SMTP_AUTH_STATE_START:
+ session_state->auth_state = SMTP_AUTH_STATE_USERNAME_REQ;
+ break;
+ case SMTP_AUTH_STATE_USERNAME_RSP:
+ session_state->auth_state = SMTP_AUTH_STATE_PASSWORD_REQ;
+ break;
+ case SMTP_AUTH_STATE_NONE:
+ case SMTP_AUTH_STATE_USERNAME_REQ:
+ case SMTP_AUTH_STATE_PASSWORD_REQ:
+ case SMTP_AUTH_STATE_PASSWORD_RSP:
+ case SMTP_AUTH_STATE_SUCCESS:
+ case SMTP_AUTH_STATE_FAILED:
+ /* ignore */
+ break;
+ }
+ } else if (session_state->auth_state == SMTP_AUTH_STATE_PASSWORD_RSP) {
+ if (code == 235) {
+ session_state->auth_state = SMTP_AUTH_STATE_SUCCESS;
+ } else {
+ session_state->auth_state = SMTP_AUTH_STATE_FAILED;
+ }
+ session_state->last_auth_frame = pinfo->fd->num;
+ }
- if (linelen >= 4) {
- proto_tree_add_item(cmdresp_tree, hf_smtp_rsp_parameter, tvb,
- offset + 4, linelen - 4, ENC_ASCII|ENC_NA);
+ /*
+ * Put the response code and parameters into the protocol tree.
+ */
+ proto_tree_add_uint(cmdresp_tree, hf_smtp_rsp_code, tvb, offset, 3,
+ code);
+
+ decrypt = NULL;
+ if (linelen >= 4) {
+ if ((stmp_decryption_enabled) && (code == 334)) {
+ decrypt = tvb_get_ephemeral_string(tvb, offset + 4, linelen - 4);
+ if (epan_base64_decode(decrypt) > 0) {
+ proto_tree_add_string(cmdresp_tree, hf_smtp_rsp_parameter, tvb,
+ offset + 4, linelen - 4, (const char*)decrypt);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%d %s", code, decrypt);
+ } else {
+ decrypt = NULL;
+ }
+ }
+
+ if (decrypt == NULL) {
+ proto_tree_add_item(cmdresp_tree, hf_smtp_rsp_parameter, tvb,
+ offset + 4, linelen - 4, ENC_ASCII|ENC_NA);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%d %s", code, tvb_get_ephemeral_string(tvb, offset + 4, linelen - 4));
+ }
+ } else {
+ col_append_str(pinfo->cinfo, COL_INFO, tvb_get_ephemeral_string(tvb, offset, linelen));
+ }
}
- }
}
-
/*
* Step past this line.
*/
@@ -882,6 +983,14 @@ proto_register_smtp(void)
{ "Response parameter", "smtp.rsp.parameter",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+ { &hf_smtp_username,
+ { "Username", "smtp.auth.username",
+ FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+
+ { &hf_smtp_password,
+ { "Password", "smtp.auth.password",
+ FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+
/* Fragment entries */
{ &hf_smtp_data_fragments,
{ "DATA fragments", "smtp.data.fragments",
@@ -959,6 +1068,10 @@ proto_register_smtp(void)
"\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
&smtp_data_desegment);
+ prefs_register_bool_preference(smtp_module, "decryption",
+ "Decrypt AUTH parameters",
+ "Whether the SMTP dissector should cecrypt AUTH parameters",
+ &stmp_decryption_enabled);
}
/* The registration hand-off routine */