Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-imap.c
Line
Count
Source
1
/* packet-imap.c
2
 * Routines for imap packet dissection
3
 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * Copied from packet-tftp.c
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
#include "config.h"
15
16
17
#include <epan/packet.h>
18
#include <epan/strutil.h>
19
#include "packet-tls.h"
20
#include "packet-tls-utils.h"
21
#include <epan/credentials.h>
22
#include <tap.h>
23
24
void proto_register_imap(void);
25
void proto_reg_handoff_imap(void);
26
27
static int proto_imap;
28
static int hf_imap_isrequest;
29
static int hf_imap_line;
30
static int hf_imap_request;
31
static int hf_imap_request_tag;
32
static int hf_imap_response;
33
static int hf_imap_response_tag;
34
static int hf_imap_request_command;
35
static int hf_imap_response_command;
36
static int hf_imap_tag;
37
static int hf_imap_command;
38
static int hf_imap_response_status;
39
static int hf_imap_request_folder;
40
static int hf_imap_request_username;
41
static int hf_imap_request_password;
42
static int hf_imap_request_uid;
43
static int hf_imap_response_in;
44
static int hf_imap_response_to;
45
static int hf_imap_time;
46
47
static int ett_imap;
48
static int ett_imap_reqresp;
49
50
static int credentials_tap;
51
52
static dissector_handle_t imap_handle;
53
static dissector_handle_t tls_handle;
54
static dissector_handle_t imf_handle;
55
56
static bool imap_ssl_heuristic = true;
57
58
/* patterns used for tvb_ws_mempbrk_pattern_uint8 */
59
static ws_mempbrk_pattern pbrk_whitespace;
60
61
14
#define TCP_PORT_IMAP     143
62
14
#define TCP_PORT_SSL_IMAP 993
63
109
#define IMAP_HEUR_LEN     5
64
136
#define NUM_LOOKAHEAD_TOKENS  3
65
66
struct simple_token_info
67
{
68
  uint8_t* token;
69
  unsigned token_start_offset;
70
  unsigned token_end_offset;
71
};
72
73
typedef struct imap_state {
74
  bool      ssl_requested;
75
  int       ssl_heur_tries_left;
76
} imap_state_t;
77
78
typedef struct imap_request_key {
79
  char* tag;
80
  uint32_t conversation;
81
} imap_request_key_t;
82
83
typedef struct imap_request_val {
84
  wmem_tree_t *frames;
85
} imap_request_val_t;
86
87
typedef struct {
88
  uint32_t req_num;
89
  uint32_t rep_num;
90
  nstime_t req_time;
91
} imap_request_info_t;
92
93
static wmem_map_t *imap_requests;
94
95
static int
96
imap_request_equal(const void *v, const void *w)
97
172
{
98
172
  const imap_request_key_t *v1 = (const imap_request_key_t*)v;
99
172
  const imap_request_key_t *v2 = (const imap_request_key_t*)w;
100
101
172
  if ((v1->conversation == v2->conversation) &&
102
172
      (!strcmp(v1->tag, v2->tag)))
103
172
    return 1;
104
105
0
  return 0;
106
172
}
107
108
static unsigned
109
imap_request_hash(const void *v)
110
998
{
111
998
  const imap_request_key_t *key = (const imap_request_key_t*)v;
112
998
  unsigned val;
113
114
998
  val = (unsigned)(wmem_str_hash(key->tag) * 37 + key->conversation * 765);
115
116
998
  return val;
117
998
}
118
119
static void
120
imap_match_request(packet_info *pinfo, proto_tree *tree, imap_request_key_t *request_key, bool is_request)
121
978
{
122
978
  imap_request_key_t  *new_request_key;
123
978
  imap_request_val_t  *request_val;
124
978
  imap_request_info_t *request_info = NULL;
125
126
978
  request_info = NULL;
127
978
  request_val = (imap_request_val_t *)wmem_map_lookup(imap_requests, request_key);
128
978
  if (!pinfo->fd->visited)
129
978
  {
130
978
    if (is_request)
131
206
    {
132
206
      if (request_val == NULL)
133
34
      {
134
34
        new_request_key = (imap_request_key_t *)wmem_memdup(wmem_file_scope(), request_key, sizeof(imap_request_key_t));
135
34
        new_request_key->tag = wmem_strdup(wmem_file_scope(), request_key->tag);
136
137
34
        request_val = wmem_new(wmem_file_scope(), imap_request_val_t);
138
34
        request_val->frames = wmem_tree_new(wmem_file_scope());
139
140
34
        wmem_map_insert(imap_requests, new_request_key, request_val);
141
34
      }
142
143
206
      request_info = wmem_new(wmem_file_scope(), imap_request_info_t);
144
206
      request_info->req_num = pinfo->num;
145
206
      request_info->rep_num = 0;
146
206
      request_info->req_time = pinfo->abs_ts;
147
206
      wmem_tree_insert32(request_val->frames, pinfo->num, (void *)request_info);
148
206
    }
149
978
    if (request_val && !is_request)
150
0
    {
151
0
      request_info = (imap_request_info_t*)wmem_tree_lookup32_le(request_val->frames, pinfo->num);
152
0
      if (request_info)
153
0
      {
154
0
        request_info->rep_num = pinfo->num;
155
0
      }
156
0
    }
157
978
  }
158
0
  else
159
0
  {
160
0
    if (request_val)
161
0
      request_info = (imap_request_info_t *)wmem_tree_lookup32_le(request_val->frames, pinfo->num);
162
0
  }
163
164
978
  if (tree && request_info)
165
206
  {
166
206
    proto_item *it;
167
168
    /* print request/response tracking in the tree */
169
206
    if (is_request)
170
206
    {
171
      /* This is a request */
172
206
      if (request_info->rep_num)
173
0
      {
174
175
0
        it = proto_tree_add_uint(tree, hf_imap_response_in, NULL, 0, 0, request_info->rep_num);
176
0
        proto_item_set_generated(it);
177
0
      }
178
206
    }
179
0
    else
180
0
    {
181
      /* This is a reply */
182
0
      if (request_info->req_num)
183
0
      {
184
0
        nstime_t    ns;
185
186
0
        it = proto_tree_add_uint(tree, hf_imap_response_to, NULL, 0, 0, request_info->req_num);
187
0
        proto_item_set_generated(it);
188
189
0
        nstime_delta(&ns, &pinfo->abs_ts, &request_info->req_time);
190
0
        it = proto_tree_add_time(tree, hf_imap_time, NULL, 0, 0, &ns);
191
0
        proto_item_set_generated(it);
192
0
      }
193
0
    }
194
206
  }
195
196
978
}
197
198
static bool
199
dissect_imap_fetch(tvbuff_t *tvb, packet_info *pinfo,
200
                            proto_tree* main_tree, proto_tree* imap_tree, proto_tree** reqresp_tree,
201
                            unsigned fetch_offset, unsigned offset, unsigned* next_offset, bool* first_line)
202
0
{
203
0
  tvbuff_t       *next_tvb;
204
0
  bool need_more = true;
205
206
  //All information in encapsulated in () so make sure there are existing and matching parenthesis
207
0
  unsigned first_parenthesis;
208
0
  if (tvb_find_uint8_remaining(tvb, fetch_offset, '(', &first_parenthesis))
209
0
  {
210
0
    unsigned remaining_size = tvb_reported_length_remaining(tvb, first_parenthesis + 1);
211
0
    if (remaining_size > 0)
212
0
    {
213
      //look for the size field
214
0
      unsigned size_start;
215
0
      if (tvb_find_uint8_length(tvb, first_parenthesis, remaining_size, '{', &size_start))
216
0
      {
217
0
        unsigned size_end;
218
0
        if (tvb_find_uint8_length(tvb, size_start + 1, remaining_size - (size_start - first_parenthesis), '}', &size_end))
219
0
        {
220
          //Have a size field, convert it to an integer to see how long the contents are
221
0
          uint32_t size = 0;
222
0
          if (tvb_get_string_uint(tvb, size_start + 1, size_end - size_start - 1, ENC_STR_DEC, &size, NULL))
223
0
          {
224
0
            unsigned remaining = tvb_reported_length_remaining(tvb, size_end + size);
225
0
            if (remaining > 0)
226
0
            {
227
              //Look for the ) after the size field
228
0
              unsigned parenthesis_end;
229
0
              if (tvb_find_uint8_length(tvb, size_end + size, remaining, ')', &parenthesis_end))
230
0
              {
231
0
                need_more = false;
232
233
                // Put the line into the protocol tree.
234
0
                proto_item *ti = proto_tree_add_item(imap_tree, hf_imap_line, tvb, offset, *next_offset - offset, ENC_ASCII);
235
0
                *reqresp_tree = proto_item_add_subtree(ti, ett_imap_reqresp);
236
237
                //no need to overwrite column information since subdissector was called
238
0
                *first_line = false;
239
240
0
                next_tvb = tvb_new_subset_length(tvb, *next_offset, size);
241
0
                call_dissector(imf_handle, next_tvb, pinfo, main_tree);
242
0
                if ((*next_offset + size) > *next_offset)
243
0
                  (*next_offset) += size;
244
0
              }
245
0
            }
246
0
          }
247
0
        }
248
0
      }
249
0
      else
250
0
      {
251
        //See if there is no size field, just and end of line
252
0
        unsigned linelen;
253
0
        if (tvb_find_line_end_remaining(tvb, first_parenthesis, &linelen, next_offset))
254
0
        {
255
0
          need_more = false;
256
257
          // Put the line into the protocol tree.
258
0
          proto_item *ti = proto_tree_add_item(imap_tree, hf_imap_line, tvb, offset, *next_offset - offset, ENC_ASCII);
259
0
          *reqresp_tree = proto_item_add_subtree(ti, ett_imap_reqresp);
260
0
        }
261
0
      }
262
0
    }
263
0
  }
264
265
0
  return need_more;
266
0
}
267
268
/* Heuristic to detect plaintext or TLS ciphertext IMAP */
269
static bool
270
check_imap_heur(tvbuff_t *tvb)
271
56
{
272
56
  if (!tvb_bytes_exist(tvb, 0, IMAP_HEUR_LEN)) {
273
3
    return true;
274
3
  }
275
276
53
  if (!tvb_ascii_isprint(tvb, 0, IMAP_HEUR_LEN))
277
4
    return false;
278
279
49
  return true;
280
53
}
281
282
static int
283
dissect_imap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
284
66
{
285
66
  bool            is_request;
286
66
  proto_tree      *imap_tree, *reqresp_tree;
287
66
  proto_item      *ti, *hidden_item;
288
66
  unsigned        offset = 0;
289
66
  unsigned        uid_offset = 0;
290
66
  unsigned        folder_offset = 0;
291
66
  unsigned        next_offset;
292
66
  unsigned        linelen, tokenlen, uidlen, uid_tokenlen, folderlen, folder_tokenlen;
293
66
  unsigned        next_token, uid_next_token, folder_next_token;
294
66
  const char     *tokenbuf = NULL;
295
66
  const char     *command_token = NULL;
296
66
  unsigned        commandlen;
297
66
  bool            first_line = true;
298
66
  imap_request_key_t request_key;
299
300
66
  conversation_t *conversation;
301
66
  imap_state_t   *session_state;
302
303
66
  conversation = find_or_create_conversation(pinfo);
304
66
  session_state = (imap_state_t *)conversation_get_proto_data(conversation, proto_imap);
305
66
  if (!session_state) {
306
49
    session_state = wmem_new0(wmem_file_scope(), imap_state_t);
307
49
    session_state->ssl_requested = false;
308
49
    if (imap_ssl_heuristic)
309
49
      session_state->ssl_heur_tries_left = 2;
310
0
    else
311
0
      session_state->ssl_heur_tries_left = -1; /* Disabled */
312
49
    conversation_add_proto_data(conversation, proto_imap, session_state);
313
49
  }
314
315
66
  request_key.tag = NULL;
316
66
  request_key.conversation = conversation->conv_index;
317
318
66
  if (imap_ssl_heuristic && session_state->ssl_heur_tries_left < 0) {
319
    /* Preference changed to enabled */
320
0
    session_state->ssl_heur_tries_left = 2;
321
0
  }
322
66
  else if (!imap_ssl_heuristic && session_state->ssl_heur_tries_left >= 0) {
323
    /* Preference changed to disabled */
324
0
    session_state->ssl_heur_tries_left = -1;
325
0
  }
326
327
  /*
328
   * It is possible the IMAP session is already running over TLS and the
329
   * STARTTLS request/response happened before the capture began. Don't assume
330
   * we have plaintext without performing some heuristic checks first.
331
   * We have three cases:
332
   *   1. capture includes STARTTLS command: no need for heuristics
333
   *   2. capture starts with STARTTLS OK response: next frame will be TLS (need to retry heuristic)
334
   *   3. capture start after STARTTLS negotiation: current frame is TLS
335
   */
336
66
  if (session_state->ssl_heur_tries_left > 0) {
337
56
    session_state->ssl_heur_tries_left--;
338
56
    if (!check_imap_heur(tvb)) {
339
4
      ssl_starttls_post_ack(tls_handle, pinfo, imap_handle);
340
4
      session_state->ssl_heur_tries_left = 0;
341
4
      return call_dissector(tls_handle, tvb, pinfo, tree);
342
4
    }
343
56
  }
344
345
62
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "IMAP");
346
347
62
  if (pinfo->match_uint == pinfo->destport)
348
26
    is_request = true;
349
36
  else
350
36
    is_request = false;
351
352
  /*
353
   * Put the first line from the buffer into the summary
354
   * (but leave out the line terminator).
355
   */
356
62
  if (!tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset))
357
7
  {
358
7
    pinfo->desegment_offset = 0;
359
7
    pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
360
7
    return tvb_captured_length(tvb);
361
7
  }
362
363
55
  ti = proto_tree_add_item(tree, proto_imap, tvb, offset, -1, ENC_NA);
364
55
  imap_tree = proto_item_add_subtree(ti, ett_imap);
365
366
55
  hidden_item = proto_tree_add_boolean(imap_tree, hf_imap_isrequest, tvb, 0, 0, is_request);
367
55
  proto_item_set_hidden(hidden_item);
368
369
1.54k
  while(tvb_offset_exists(tvb, offset)) {
370
371
1.53k
    commandlen = 0;
372
1.53k
    folder_offset = 0;
373
1.53k
    folder_tokenlen = 0;
374
375
    /*
376
     * Find the end of each line
377
     *
378
     * Note that "tvb_find_line_end_remaining()" will return a value that is
379
     * not longer than what's in the buffer, so the "tvb_get_ptr()"
380
     * call won't throw an exception.
381
     */
382
1.53k
    if (!tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset))
383
51
    {
384
51
      pinfo->desegment_offset = offset;
385
51
      pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
386
51
      return tvb_captured_length(tvb);
387
51
    }
388
389
    /*
390
     * Check that the line doesn't begin with '*', because that's a continuation line.
391
     * Otherwise if a tag is present then extract tokens.
392
     */
393
1.48k
    if (tvb_get_uint8(tvb, offset) == '*') {
394
47
      bool show_line = true;
395
396
      //find up to NUM_LOOKAHEAD_TOKENS tokens
397
47
      unsigned start_offset;
398
47
      unsigned next_pattern = offset, token_count = 0;
399
47
      struct simple_token_info tokens[NUM_LOOKAHEAD_TOKENS];
400
47
      bool found;
401
47
      do
402
160
      {
403
160
        start_offset = next_pattern+1;
404
160
        found = tvb_ws_mempbrk_uint8_length(tvb, start_offset, next_offset - start_offset, &pbrk_whitespace, &next_pattern, NULL);
405
160
        if (next_pattern > start_offset)
406
94
        {
407
94
          tokens[token_count].token = tvb_get_string_enc(pinfo->pool, tvb, start_offset, next_pattern-start_offset, ENC_ASCII);
408
94
          tokens[token_count].token_start_offset = start_offset;
409
94
          tokens[token_count].token_end_offset = next_pattern;
410
94
          token_count++;
411
94
        }
412
160
      } while ((found != false) && (token_count < NUM_LOOKAHEAD_TOKENS));
413
414
47
      if (token_count >= 2)
415
28
      {
416
28
        bool need_more = false;
417
107
        for (unsigned token = 0; token < token_count; token++)
418
79
        {
419
79
          if (!tvb_strncaseeql(tvb, tokens[token].token_start_offset, "FETCH", tokens[token].token_end_offset - tokens[token].token_start_offset))
420
0
          {
421
            //FETCH command.  Presume we need more data until we find a complete command
422
0
            need_more = dissect_imap_fetch(tvb, pinfo, tree, imap_tree, &reqresp_tree,
423
0
                                           tokens[token].token_end_offset, offset, &next_offset, &first_line);
424
0
            if (!need_more)
425
0
            {
426
0
              show_line = false;
427
0
            }
428
0
            break;
429
0
          }
430
79
        }
431
432
28
        if (need_more)
433
0
        {
434
0
          pinfo->desegment_offset = offset;
435
0
          pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
436
0
          return tvb_captured_length(tvb);
437
0
        }
438
28
      }
439
440
47
      if (show_line)
441
47
        proto_tree_add_item(imap_tree, hf_imap_line, tvb, offset, next_offset - offset, ENC_ASCII);
442
443
1.43k
    } else {
444
445
1.43k
      if (first_line) {
446
54
        col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", is_request ? "Request" : "Response", tvb_format_text(pinfo->pool, tvb, offset, linelen));
447
54
        first_line = false;
448
54
      }
449
450
      // Put the line into the protocol tree.
451
1.43k
      ti = proto_tree_add_item(imap_tree, hf_imap_line, tvb, offset, next_offset - offset, ENC_ASCII);
452
1.43k
      reqresp_tree = proto_item_add_subtree(ti, ett_imap_reqresp);
453
454
      /*
455
       * Show each line as requests or replies + tags.
456
       */
457
458
      /*
459
       * Add the line as request or reply data.
460
       */
461
1.43k
      if (linelen != 0) {
462
366
        proto_tree_add_item(reqresp_tree, (is_request) ? hf_imap_request : hf_imap_response, tvb, offset, linelen, ENC_ASCII|ENC_NA);
463
366
      }
464
465
      /*
466
       * Extract the first token, and, if there is a first
467
       * token, add it as the request or reply tag.
468
       */
469
1.43k
      if (tvb_get_token_len_length(tvb, offset, linelen, &tokenlen, &next_token)) {
470
167
        const char* tag = (const char*)tvb_get_string_enc(pinfo->pool, tvb, offset, tokenlen, ENC_ASCII);
471
167
        request_key.tag = wmem_ascii_strdown(pinfo->pool, tag, strlen(tag));
472
473
167
        proto_tree_add_string(reqresp_tree, (is_request) ? hf_imap_request_tag : hf_imap_response_tag, tvb, offset, tokenlen, tag);
474
167
        hidden_item = proto_tree_add_string(reqresp_tree, hf_imap_tag, tvb, offset, tokenlen, tag);
475
167
        proto_item_set_hidden(hidden_item);
476
477
167
        linelen -= (next_token-offset);
478
167
        offset = next_token;
479
167
      }
480
481
      /*
482
       * Extract second token, and, if there is a second
483
       * token, and it's not uid, add it as the request or reply command.
484
       */
485
1.43k
      if (tvb_get_token_len_length(tvb, offset, linelen, &tokenlen, &next_token)) {
486
487
130
        tokenbuf = (const char*)tvb_get_string_enc(pinfo->pool, tvb, offset, tokenlen, ENC_ASCII);
488
130
        tokenbuf = wmem_ascii_strdown(pinfo->pool, tokenbuf, tokenlen);
489
490
130
        if (is_request && !tvb_strncaseeql(tvb, offset, "UID", tokenlen)) {
491
0
          proto_tree_add_item(reqresp_tree, hf_imap_request_uid, tvb, offset, tokenlen, ENC_ASCII|ENC_NA);
492
          /*
493
           * UID is a precursor to a command, if following the tag,
494
           * so move to next token to grab the actual command.
495
           */
496
0
          uidlen = linelen - (next_token - offset);
497
0
          uid_offset = next_token;
498
0
          if (tvb_get_token_len_length(tvb, next_token, uidlen, &uid_tokenlen, &uid_next_token)) {
499
0
            proto_tree_add_item(reqresp_tree, hf_imap_request_command, tvb, uid_offset, uid_tokenlen, ENC_ASCII);
500
0
            hidden_item = proto_tree_add_item(reqresp_tree, hf_imap_command, tvb, offset, tokenlen, ENC_ASCII);
501
0
            proto_item_set_hidden(hidden_item);
502
503
            /*
504
             * Save command string to do specialized processing.
505
             */
506
0
            commandlen = uid_tokenlen;
507
0
            command_token = (const char*)tvb_get_string_enc(pinfo->pool, tvb, next_token, commandlen, ENC_ASCII);
508
0
            command_token = wmem_ascii_strdown(pinfo->pool, command_token, commandlen);
509
510
0
            folderlen = linelen - (uid_next_token - offset);
511
0
            folder_offset = uid_next_token;
512
0
            tvb_get_token_len_length(tvb, uid_next_token, folderlen, &folder_tokenlen, &folder_next_token);
513
0
          }
514
130
        } else {
515
          /*
516
           * Not a UID request so perform normal parsing.
517
           */
518
130
          proto_tree_add_item(reqresp_tree, (is_request) ? hf_imap_request_command : hf_imap_response_status, tvb, offset, tokenlen, ENC_ASCII|ENC_NA);
519
130
          if (is_request) {
520
33
            hidden_item = proto_tree_add_item(reqresp_tree, hf_imap_command, tvb, offset, tokenlen, ENC_ASCII);
521
33
            proto_item_set_hidden(hidden_item);
522
523
            /*
524
             * Save command string to do specialized processing.
525
             */
526
33
            commandlen = tokenlen;
527
33
            command_token = (const char*)tvb_get_string_enc(pinfo->pool, tvb, offset, commandlen, ENC_ASCII);
528
33
            command_token = wmem_ascii_strdown(pinfo->pool, command_token, commandlen);
529
530
33
            folderlen = linelen - (next_token - offset);
531
33
            folder_offset = next_token;
532
33
            tvb_get_token_len_length(tvb, next_token, folderlen, &folder_tokenlen, &folder_next_token);
533
33
          }
534
130
        }
535
536
130
        if (commandlen > 0) { // implies is_request (i.e. can be true only if is_request but is not equivalent)
537
28
          if (strncmp(command_token, "select", commandlen) == 0 ||
538
28
              strncmp(command_token, "examine", commandlen) == 0 ||
539
28
              strncmp(command_token, "create", commandlen) == 0 ||
540
28
              strncmp(command_token, "delete", commandlen) == 0 ||
541
28
              strncmp(command_token, "rename", commandlen) == 0 ||
542
28
              strncmp(command_token, "subscribe", commandlen) == 0 ||
543
28
              strncmp(command_token, "unsubscribe", commandlen) == 0 ||
544
28
              strncmp(command_token, "status", commandlen) == 0 ||
545
28
              strncmp(command_token, "append", commandlen) == 0 ||
546
22
              strncmp(command_token, "search", commandlen) == 0) {
547
548
            /*
549
             * These commands support folder as an argument,
550
             * so parse out the folder name.
551
             */
552
6
            if (folder_tokenlen != 0)
553
6
              proto_tree_add_item(reqresp_tree, hf_imap_request_folder, tvb, folder_offset, folder_tokenlen, ENC_ASCII);
554
6
          }
555
22
          else if ((linelen > 0) && strncmp(command_token, "copy", commandlen) == 0) {
556
            /*
557
             * Handle the copy command separately since folder
558
             * is the second argument for this command.
559
             */
560
0
            folderlen = linelen - (folder_next_token - offset);
561
0
            folder_offset = folder_next_token;
562
0
            folder_tokenlen = tvb_get_token_len_length(tvb, folder_offset, folderlen, &folder_tokenlen , &folder_next_token);
563
564
0
            if (folder_tokenlen != 0)
565
0
              proto_tree_add_item(reqresp_tree, hf_imap_request_folder, tvb, folder_offset, folder_tokenlen, ENC_ASCII);
566
0
          }
567
22
          else if (strncmp(command_token, "starttls", commandlen) == 0) {
568
            /* If next response is OK, then TLS should be commenced. */
569
0
            session_state->ssl_requested = true;
570
0
          }
571
22
          else if (strncmp(command_token, "login", commandlen) == 0) {
572
0
            unsigned usernamelen = linelen - (next_token - offset);
573
0
            unsigned username_offset = next_token;
574
0
            unsigned username_next_token;
575
0
            unsigned username_tokenlen;
576
0
            tvb_get_token_len_length(tvb, next_token, usernamelen, &username_tokenlen, &username_next_token);
577
0
            char *username = (char*)tvb_get_string_enc(pinfo->pool, tvb, username_offset, username_tokenlen, ENC_ASCII | ENC_NA);
578
0
            proto_tree_add_string(reqresp_tree, hf_imap_request_username, tvb, username_offset, username_tokenlen, username);
579
580
0
            unsigned passwordlen = linelen - (username_next_token - offset);
581
0
            unsigned password_offset = username_next_token;
582
0
            unsigned password_tokenlen;
583
0
            tvb_get_token_len_length(tvb, username_next_token, passwordlen, NULL, &password_tokenlen);
584
0
            const char* password = (char*)tvb_get_string_enc(pinfo->pool, tvb, password_offset + 1, password_tokenlen - 2, ENC_ASCII | ENC_NA);
585
0
            proto_tree_add_string(reqresp_tree, hf_imap_request_password, tvb, password_offset, password_tokenlen, password);
586
587
0
            tap_credential_t* auth = wmem_new0(pinfo->pool, tap_credential_t);
588
0
            auth->num = auth->username_num = pinfo->num;
589
0
            auth->password_hf_id = hf_imap_request_password;
590
0
            auth->username = username;
591
0
            auth->proto = "IMAP";
592
0
            tap_queue_packet(credentials_tap, pinfo, auth);
593
0
          }
594
28
        }
595
596
130
        if (!is_request) {
597
          //See if there is the response command
598
97
          unsigned command_next_token;
599
97
          unsigned command_offset = next_token;
600
97
          commandlen = linelen - (next_token-offset);
601
97
          tvb_get_token_len_length(tvb, next_token, commandlen, &commandlen , &command_next_token);
602
97
          if (commandlen > 0) {
603
88
            proto_tree_add_item(reqresp_tree, hf_imap_response_command, tvb, command_offset, commandlen, ENC_ASCII);
604
88
            hidden_item = proto_tree_add_item(reqresp_tree, hf_imap_command, tvb, command_offset, commandlen, ENC_ASCII);
605
88
            proto_item_set_hidden(hidden_item);
606
88
          }
607
97
        }
608
609
        /* If not yet switched to TLS, check for STARTTLS. */
610
130
        if (session_state->ssl_requested) {
611
0
          if (!is_request && (tokenbuf != NULL) && strncmp(tokenbuf, "ok", tokenlen) == 0) {
612
            /* STARTTLS accepted, next reply will be TLS. */
613
0
            ssl_starttls_ack(tls_handle, pinfo, imap_handle);
614
0
            if (session_state->ssl_heur_tries_left > 0) {
615
0
              session_state->ssl_heur_tries_left = 0;
616
0
            }
617
0
             session_state->ssl_requested = false;
618
0
          }
619
0
        }
620
130
      }
621
622
      /* Add request/response statistics */
623
1.43k
      if (request_key.tag != NULL)
624
978
      {
625
978
        imap_match_request(pinfo, reqresp_tree, &request_key, is_request);
626
978
      }
627
1.43k
    }
628
629
1.48k
    offset = next_offset; /* Skip over last line and \r\n at the end of it */
630
1.48k
  }
631
632
  // If there is only lines that begin with *, at least show the first one
633
4
  if (first_line) {
634
0
    col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", is_request ? "Request" : "Response", tvb_format_text(pinfo->pool, tvb, 0, linelen));
635
0
  }
636
637
4
  return tvb_captured_length(tvb);
638
55
}
639
640
void
641
proto_register_imap(void)
642
14
{
643
14
  static hf_register_info hf[] = {
644
645
14
    { &hf_imap_isrequest,
646
14
      { "Request", "imap.isrequest",
647
14
         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
648
14
         "true if IMAP request, false otherwise", HFILL }
649
14
    },
650
14
    { &hf_imap_line,
651
14
      { "Line", "imap.line",
652
14
        FT_STRINGZ, BASE_NONE, NULL, 0x0,
653
14
        "A line of an IMAP message", HFILL }
654
14
    },
655
14
    { &hf_imap_request,
656
14
      { "Request", "imap.request",
657
14
        FT_STRINGZ, BASE_NONE, NULL, 0x0,
658
14
        "Remainder of request line", HFILL }
659
14
    },
660
14
    { &hf_imap_request_tag,
661
14
      { "Request Tag", "imap.request_tag",
662
14
        FT_STRINGZ, BASE_NONE, NULL, 0x0,
663
14
        "First token of request line", HFILL }
664
14
    },
665
14
    { &hf_imap_response,
666
14
      { "Response", "imap.response",
667
14
        FT_STRINGZ, BASE_NONE, NULL, 0x0,
668
14
        "Remainder of response line", HFILL }
669
14
    },
670
14
    { &hf_imap_response_tag,
671
14
      { "Response Tag", "imap.response_tag",
672
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
673
14
      "First token of response line", HFILL }
674
14
    },
675
14
    { &hf_imap_request_command,
676
14
      { "Request Command", "imap.request.command",
677
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
678
14
      "Request command name", HFILL }
679
14
    },
680
14
    { &hf_imap_response_command,
681
14
      { "Response Command", "imap.response.command",
682
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
683
14
      "Response command name", HFILL }
684
14
    },
685
14
    { &hf_imap_response_status,
686
14
      { "Response Status", "imap.response.status",
687
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
688
14
      "Response status code", HFILL }
689
14
    },
690
14
    { &hf_imap_tag,
691
14
      { "Tag", "imap.tag",
692
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
693
14
      "First token of line", HFILL }
694
14
    },
695
14
    { &hf_imap_command,
696
14
      { "Command", "imap.command",
697
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
698
14
      "Request or Response command name", HFILL }
699
14
    },
700
14
    { &hf_imap_request_folder,
701
14
      { "Request Folder", "imap.request.folder",
702
14
        FT_STRINGZ, BASE_NONE, NULL, 0x0,
703
14
        "Request command folder", HFILL }
704
14
    },
705
14
    { &hf_imap_request_uid,
706
14
      { "Request isUID", "imap.request.command.uid",
707
14
      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
708
14
      "Request command uid", HFILL }
709
14
    },
710
14
    { &hf_imap_request_username,
711
14
      { "Request Username", "imap.request.username",
712
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
713
14
      "Request command username", HFILL }
714
14
    },
715
14
    { &hf_imap_request_password,
716
14
      { "Request Password", "imap.request.password",
717
14
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
718
14
      "Request command password", HFILL }
719
14
    },
720
721
    /* Request/Response Matching */
722
14
    { &hf_imap_response_in,
723
14
      { "Response In", "imap.response_in",
724
14
      FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
725
14
      "The response to this IMAP request is in this frame", HFILL }
726
14
    },
727
14
    { &hf_imap_response_to,
728
14
      { "Request In", "imap.response_to",
729
14
      FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
730
14
      "This is a response to the IMAP request in this frame", HFILL }
731
14
    },
732
14
    { &hf_imap_time,
733
14
      { "Response Time", "imap.time",
734
14
      FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
735
14
      "The time between the request and response", HFILL }
736
14
    },
737
14
  };
738
739
14
  static int *ett[] = {
740
14
    &ett_imap,
741
14
    &ett_imap_reqresp,
742
14
  };
743
744
14
  module_t *imap_module;
745
746
14
  proto_imap = proto_register_protocol("Internet Message Access Protocol", "IMAP", "imap");
747
748
14
  imap_handle = register_dissector("imap", dissect_imap, proto_imap);
749
750
14
  proto_register_field_array(proto_imap, hf, array_length(hf));
751
14
  proto_register_subtree_array(ett, array_length(ett));
752
753
14
  imap_module = prefs_register_protocol(proto_imap, NULL);
754
14
  prefs_register_bool_preference(imap_module, "ssl_heuristic",
755
14
                                   "Use heuristic detection for TLS",
756
14
                                   "Whether to use heuristics for post-STARTTLS detection of encrypted IMAP conversations",
757
14
                                   &imap_ssl_heuristic);
758
759
14
  imap_requests = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), imap_request_hash, imap_request_equal);
760
761
  /* compile patterns */
762
14
  ws_mempbrk_compile(&pbrk_whitespace, " \t\r\n");
763
764
14
  credentials_tap = register_tap("credentials");
765
14
}
766
767
void
768
proto_reg_handoff_imap(void)
769
14
{
770
14
  dissector_add_uint_with_preference("tcp.port", TCP_PORT_IMAP, imap_handle);
771
14
  ssl_dissector_add(TCP_PORT_SSL_IMAP, imap_handle);
772
14
  tls_handle = find_dissector("tls");
773
14
  imf_handle = find_dissector("imf");
774
14
}
775
/*
776
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
777
 *
778
 * Local variables:
779
 * c-basic-offset: 2
780
 * tab-width: 8
781
 * indent-tabs-mode: nil
782
 * End:
783
 *
784
 * vi: set shiftwidth=2 tabstop=8 expandtab:
785
 * :indentSize=2:tabSize=8:noTabs=true
786
 */