nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* export_object_smb.c |
2 | * Routines for tracking & saving objects (files) found in SMB streams |
||
3 | * See also: export_object.c / export_object.h for common code |
||
4 | * Initial file, prototypes and general structure initially copied |
||
5 | * from export_object_http.c |
||
6 | * |
||
7 | * Copyright 2010, David Perez & Jose Pico from TADDONG S.L. |
||
8 | * |
||
9 | * Wireshark - Network traffic analyzer |
||
10 | * By Gerald Combs <gerald@wireshark.org> |
||
11 | * Copyright 1998 Gerald Combs |
||
12 | * |
||
13 | * This program is free software; you can redistribute it and/or |
||
14 | * modify it under the terms of the GNU General Public License |
||
15 | * as published by the Free Software Foundation; either version 2 |
||
16 | * of the License, or (at your option) any later version. |
||
17 | * |
||
18 | * This program is distributed in the hope that it will be useful, |
||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
21 | * GNU General Public License for more details. |
||
22 | * |
||
23 | * You should have received a copy of the GNU General Public License |
||
24 | * along with this program; if not, write to the Free Software |
||
25 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
||
26 | * USA. |
||
27 | */ |
||
28 | |||
29 | #include "config.h" |
||
30 | |||
31 | |||
32 | #include <epan/packet.h> |
||
33 | #include <epan/dissectors/packet-smb2.h> |
||
34 | #include <epan/tap.h> |
||
35 | |||
36 | #include "export_object.h" |
||
37 | |||
38 | |||
39 | /* These flags show what kind of data the object contains |
||
40 | (designed to be or'ed) */ |
||
41 | #define SMB_EO_CONTAINS_NOTHING 0x00 |
||
42 | #define SMB_EO_CONTAINS_READS 0x01 |
||
43 | #define SMB_EO_CONTAINS_WRITES 0x02 |
||
44 | #define SMB_EO_CONTAINS_READSANDWRITES 0x03 |
||
45 | #define LEGAL_FILENAME_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.- /\\{}[]=()&%$!,;.+&%$~#@" |
||
46 | |||
47 | static const value_string smb_eo_contains_string[] = { |
||
48 | {SMB_EO_CONTAINS_NOTHING, "" }, |
||
49 | {SMB_EO_CONTAINS_READS, "R" }, |
||
50 | {SMB_EO_CONTAINS_WRITES, "W" }, |
||
51 | {SMB_EO_CONTAINS_READSANDWRITES, "R&W"}, |
||
52 | {0, NULL} |
||
53 | }; |
||
54 | |||
55 | /* Strings that describes the SMB object type */ |
||
56 | static const value_string smb_fid_types[] = { |
||
57 | {SMB_FID_TYPE_UNKNOWN,"UNKNOWN"}, |
||
58 | {SMB_FID_TYPE_FILE,"FILE"}, |
||
59 | {SMB_FID_TYPE_DIR,"DIRECTORY (Not Implemented)"}, |
||
60 | {SMB_FID_TYPE_PIPE,"PIPE (Not Implemented)"}, |
||
61 | {0, NULL} |
||
62 | }; |
||
63 | |||
64 | static const value_string smb2_fid_types[] = { |
||
65 | {SMB2_FID_TYPE_UNKNOWN,"UNKNOWN"}, |
||
66 | {SMB2_FID_TYPE_FILE,"FILE"}, |
||
67 | {SMB2_FID_TYPE_DIR,"DIRECTORY (Not Implemented)"}, |
||
68 | {SMB2_FID_TYPE_PIPE,"PIPE (Not Implemented)"}, |
||
69 | {SMB2_FID_TYPE_OTHER,"OTHER (Not Implemented)"}, |
||
70 | {0, NULL} |
||
71 | }; |
||
72 | |||
73 | /* This struct contains the relationship between |
||
74 | the row# in the export_object window and the file being captured; |
||
75 | the row# in this GSList will match the row# in the entry list */ |
||
76 | |||
77 | typedef struct _active_file { |
||
78 | guint16 tid, uid, fid; |
||
79 | guint64 file_length; /* The last free reported offset */ |
||
80 | /* We treat it as the file length */ |
||
81 | guint64 data_gathered; /* The actual total of data gathered */ |
||
82 | guint8 flag_contains; /* What kind of data it contains */ |
||
83 | GSList *free_chunk_list; /* A list of virtual "holes" in the */ |
||
84 | /* file stream stored in memory */ |
||
85 | gboolean is_out_of_memory; /* TRUE if we cannot allocate memory */ |
||
86 | /* memory for this file */ |
||
87 | } active_file ; |
||
88 | |||
89 | /* This is the GSList that will contain all the files that we are tracking */ |
||
90 | static GSList *GSL_active_files = NULL; |
||
91 | |||
92 | /* We define a free chunk in a file as an start offset and end offset |
||
93 | Consider a free chunk as a "hole" in a file that we are capturing */ |
||
94 | typedef struct _free_chunk { |
||
95 | guint64 start_offset; |
||
96 | guint64 end_offset; |
||
97 | } free_chunk; |
||
98 | |||
99 | /* insert_chunk function will recalculate the free_chunk_list, the data_size, |
||
100 | the end_of_file, and the data_gathered as appropriate. |
||
101 | It will also insert the data chunk that is coming in the right |
||
102 | place of the file in memory. |
||
103 | HINTS: |
||
104 | file->data_gathered contains the real data gathered independently |
||
105 | from the file length |
||
106 | file->file_length contains the length of the file in memory, i.e., |
||
107 | the last offset captured. In most cases, the real |
||
108 | file length would be different. |
||
109 | */ |
||
110 | static void |
||
111 | insert_chunk(active_file *file, export_object_entry_t *entry, const smb_eo_t *eo_info) |
||
112 | { |
||
113 | gint nfreechunks = g_slist_length(file->free_chunk_list); |
||
114 | gint i; |
||
115 | free_chunk *current_free_chunk; |
||
116 | free_chunk *new_free_chunk; |
||
117 | guint64 chunk_offset = eo_info->smb_file_offset; |
||
118 | guint64 chunk_length = eo_info->payload_len; |
||
119 | guint64 chunk_end_offset = chunk_offset + chunk_length-1; |
||
120 | /* Size of file in memory */ |
||
121 | guint64 calculated_size = chunk_offset + chunk_length; |
||
122 | gpointer dest_memory_addr; |
||
123 | |||
124 | /* Let's recalculate the file length and data gathered */ |
||
125 | if ((file->data_gathered == 0) && (nfreechunks == 0)) { |
||
126 | /* If this is the first entry for this file, we first |
||
127 | create an initial free chunk */ |
||
128 | new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk)); |
||
129 | new_free_chunk->start_offset = 0; |
||
130 | new_free_chunk->end_offset = MAX(file->file_length, chunk_end_offset+1) - 1; |
||
131 | file->free_chunk_list = NULL; |
||
132 | file->free_chunk_list = g_slist_append(file->free_chunk_list, new_free_chunk); |
||
133 | nfreechunks += 1; |
||
134 | } else { |
||
135 | if (chunk_end_offset > file->file_length-1) { |
||
136 | new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk)); |
||
137 | new_free_chunk->start_offset = file->file_length; |
||
138 | new_free_chunk->end_offset = chunk_end_offset; |
||
139 | file->free_chunk_list = g_slist_append(file->free_chunk_list, new_free_chunk); |
||
140 | nfreechunks += 1; |
||
141 | } |
||
142 | } |
||
143 | file->file_length = MAX(file->file_length, chunk_end_offset+1); |
||
144 | |||
145 | /* Recalculate each free chunk according with the incoming data chunk */ |
||
146 | for (i=0; i<nfreechunks; i++) { |
||
147 | current_free_chunk = (free_chunk *)g_slist_nth_data(file->free_chunk_list, i); |
||
148 | /* 1. data chunk before the free chunk? */ |
||
149 | /* -> free chunk is not altered and no new data gathered */ |
||
150 | if (chunk_end_offset<current_free_chunk->start_offset) { |
||
151 | continue; |
||
152 | } |
||
153 | /* 2. data chunk overlaps the first part of free_chunk */ |
||
154 | /* -> free chunk shrinks from the beginning */ |
||
155 | if (chunk_offset<=current_free_chunk->start_offset && chunk_end_offset>=current_free_chunk->start_offset && chunk_end_offset<current_free_chunk->end_offset) { |
||
156 | file->data_gathered += chunk_end_offset-current_free_chunk->start_offset+1; |
||
157 | current_free_chunk->start_offset=chunk_end_offset+1; |
||
158 | continue; |
||
159 | } |
||
160 | /* 3. data chunk overlaps completely the free chunk */ |
||
161 | /* -> free chunk is removed */ |
||
162 | if (chunk_offset<=current_free_chunk->start_offset && chunk_end_offset>=current_free_chunk->end_offset) { |
||
163 | file->data_gathered += current_free_chunk->end_offset-current_free_chunk->start_offset+1; |
||
164 | file->free_chunk_list = g_slist_remove(file->free_chunk_list, current_free_chunk); |
||
165 | nfreechunks -= 1; |
||
166 | if (nfreechunks == 0) { /* The free chunk list is empty */ |
||
167 | g_slist_free(file->free_chunk_list); |
||
168 | file->free_chunk_list = NULL; |
||
169 | break; |
||
170 | } |
||
171 | i--; |
||
172 | continue; |
||
173 | } |
||
174 | /* 4. data chunk is inside the free chunk */ |
||
175 | /* -> free chunk is splitted into two */ |
||
176 | if (chunk_offset>current_free_chunk->start_offset && chunk_end_offset<current_free_chunk->end_offset) { |
||
177 | new_free_chunk = (free_chunk *)g_malloc(sizeof(free_chunk)); |
||
178 | new_free_chunk->start_offset = chunk_end_offset + 1; |
||
179 | new_free_chunk->end_offset = current_free_chunk->end_offset; |
||
180 | current_free_chunk->end_offset = chunk_offset-1; |
||
181 | file->free_chunk_list = g_slist_insert(file->free_chunk_list, new_free_chunk, i + 1); |
||
182 | file->data_gathered += chunk_length; |
||
183 | continue; |
||
184 | } |
||
185 | /* 5.- data chunk overlaps the end part of free chunk */ |
||
186 | /* -> free chunk shrinks from the end */ |
||
187 | if (chunk_offset>current_free_chunk->start_offset && chunk_offset<=current_free_chunk->end_offset && chunk_end_offset>=current_free_chunk->end_offset) { |
||
188 | file->data_gathered += current_free_chunk->end_offset-chunk_offset+1; |
||
189 | current_free_chunk->end_offset = chunk_offset-1; |
||
190 | continue; |
||
191 | } |
||
192 | /* 6.- data chunk is after the free chunk */ |
||
193 | /* -> free chunk is not altered and no new data gathered */ |
||
194 | if (chunk_offset>current_free_chunk->end_offset) { |
||
195 | continue; |
||
196 | } |
||
197 | } |
||
198 | |||
199 | /* Now, let's insert the data chunk into memory |
||
200 | ...first, we shall be able to allocate the memory */ |
||
201 | if (!entry->payload_data) { |
||
202 | /* This is a New file */ |
||
203 | if (calculated_size > G_MAXUINT32) { |
||
204 | /* |
||
205 | * The argument to g_try_malloc() is |
||
206 | * a gsize, however the maximum size of a file |
||
207 | * is 32-bit. If the calculated size is |
||
208 | * bigger than that, we just say the attempt |
||
209 | * to allocate memory failed. |
||
210 | */ |
||
211 | entry->payload_data = NULL; |
||
212 | } else { |
||
213 | entry->payload_data = (guint8 *)g_try_malloc((gsize)calculated_size); |
||
214 | entry->payload_len = calculated_size; |
||
215 | } |
||
216 | if (!entry->payload_data) { |
||
217 | /* Memory error */ |
||
218 | file->is_out_of_memory = TRUE; |
||
219 | } |
||
220 | } else { |
||
221 | /* This is an existing file in memory */ |
||
222 | if (calculated_size > (guint64) entry->payload_len && |
||
223 | !file->is_out_of_memory) { |
||
224 | /* We need more memory */ |
||
225 | if (calculated_size > G_MAXUINT32) { |
||
226 | /* |
||
227 | * As for g_try_malloc(), so for |
||
228 | * g_try_realloc(). |
||
229 | */ |
||
230 | dest_memory_addr = NULL; |
||
231 | } else { |
||
232 | dest_memory_addr = g_try_realloc( |
||
233 | entry->payload_data, |
||
234 | (gsize)calculated_size); |
||
235 | } |
||
236 | if (!dest_memory_addr) { |
||
237 | /* Memory error */ |
||
238 | file->is_out_of_memory = TRUE; |
||
239 | /* We don't have memory for this file. |
||
240 | Free the current file content from memory */ |
||
241 | g_free(entry->payload_data); |
||
242 | entry->payload_data = NULL; |
||
243 | entry->payload_len = 0; |
||
244 | } else { |
||
245 | entry->payload_data = (guint8 *)dest_memory_addr; |
||
246 | entry->payload_len = calculated_size; |
||
247 | } |
||
248 | } |
||
249 | } |
||
250 | /* ...then, put the chunk of the file in the right place */ |
||
251 | if (!file->is_out_of_memory) { |
||
252 | dest_memory_addr = entry->payload_data + chunk_offset; |
||
253 | memmove(dest_memory_addr, eo_info->payload_data, eo_info->payload_len); |
||
254 | } |
||
255 | } |
||
256 | |||
257 | /* We use this function to obtain the index in the GSL of a given file */ |
||
258 | static int |
||
259 | find_incoming_file(GSList *GSL_active_files_p, active_file *incoming_file) |
||
260 | { |
||
261 | int i, row, last; |
||
262 | active_file *in_list_file; |
||
263 | |||
264 | row = -1; |
||
265 | last = g_slist_length(GSL_active_files_p) - 1; |
||
266 | |||
267 | /* We lookup in reverse order because it is more likely that the file |
||
268 | is one of the latest */ |
||
269 | for (i=last; i>=0; i--) { |
||
270 | in_list_file = (active_file *)g_slist_nth_data(GSL_active_files_p, i); |
||
271 | /* The best-working criteria of two identical files is that the file |
||
272 | that is the same of the file that we are analyzing is the last one |
||
273 | in the list that has the same tid and the same fid */ |
||
274 | /* note that we have excluded in_list_file->uid == incoming_file->uid |
||
275 | from the comparison, because a file can be opened by different |
||
276 | SMB users and it is still the same file */ |
||
277 | if (in_list_file->tid == incoming_file->tid && |
||
278 | in_list_file->fid == incoming_file->fid) { |
||
279 | row = i; |
||
280 | break; |
||
281 | } |
||
282 | } |
||
283 | |||
284 | return row; |
||
285 | } |
||
286 | |||
287 | /* This is the function answering to the registered tap listener call */ |
||
288 | gboolean |
||
289 | eo_smb_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data) |
||
290 | { |
||
291 | export_object_list_t *object_list = (export_object_list_t *)tapdata; |
||
292 | const smb_eo_t *eo_info = (const smb_eo_t *)data; |
||
293 | |||
294 | export_object_entry_t *entry; |
||
295 | export_object_entry_t *current_entry; |
||
296 | active_file incoming_file; |
||
297 | gint active_row; |
||
298 | active_file *new_file; |
||
299 | active_file *current_file; |
||
300 | guint8 contains; |
||
301 | gboolean is_supported_filetype; |
||
302 | gfloat percent; |
||
303 | |||
304 | gchar *aux_smb_fid_type_string; |
||
305 | |||
306 | if (eo_info->smbversion==1) { |
||
307 | /* Is this an eo_smb supported file_type? (right now we only support FILE) */ |
||
308 | is_supported_filetype = (eo_info->fid_type == SMB_FID_TYPE_FILE); |
||
309 | aux_smb_fid_type_string=g_strdup(try_val_to_str(eo_info->fid_type, smb_fid_types)); |
||
310 | |||
311 | /* What kind of data this packet contains? */ |
||
312 | switch(eo_info->cmd) { |
||
313 | case SMB_COM_READ_ANDX: |
||
314 | case SMB_COM_READ: |
||
315 | contains = SMB_EO_CONTAINS_READS; |
||
316 | break; |
||
317 | case SMB_COM_WRITE_ANDX: |
||
318 | case SMB_COM_WRITE: |
||
319 | contains = SMB_EO_CONTAINS_WRITES; |
||
320 | break; |
||
321 | default: |
||
322 | contains = SMB_EO_CONTAINS_NOTHING; |
||
323 | break; |
||
324 | } |
||
325 | } else { |
||
326 | /* Is this an eo_smb supported file_type? (right now we only support FILE) */ |
||
327 | is_supported_filetype = (eo_info->fid_type == SMB2_FID_TYPE_FILE ); |
||
328 | aux_smb_fid_type_string=g_strdup(try_val_to_str(eo_info->fid_type, smb2_fid_types)); |
||
329 | |||
330 | /* What kind of data this packet contains? */ |
||
331 | switch(eo_info->cmd) { |
||
332 | case SMB2_COM_READ: |
||
333 | contains = SMB_EO_CONTAINS_READS; |
||
334 | break; |
||
335 | case SMB2_COM_WRITE: |
||
336 | contains = SMB_EO_CONTAINS_WRITES; |
||
337 | break; |
||
338 | default: |
||
339 | contains = SMB_EO_CONTAINS_NOTHING; |
||
340 | break; |
||
341 | } |
||
342 | } |
||
343 | |||
344 | |||
345 | /* Is this data from an already tracked file or not? */ |
||
346 | incoming_file.tid = eo_info->tid; |
||
347 | incoming_file.uid = eo_info->uid; |
||
348 | incoming_file.fid = eo_info->fid; |
||
349 | active_row = find_incoming_file(GSL_active_files, &incoming_file); |
||
350 | |||
351 | if (active_row == -1) { /* This is a new-tracked file */ |
||
352 | /* Construct the entry in the list of active files */ |
||
353 | entry = (export_object_entry_t *)g_malloc(sizeof(export_object_entry_t)); |
||
354 | entry->payload_data = NULL; |
||
355 | entry->payload_len = 0; |
||
356 | new_file = (active_file *)g_malloc(sizeof(active_file)); |
||
357 | new_file->tid = incoming_file.tid; |
||
358 | new_file->uid = incoming_file.uid; |
||
359 | new_file->fid = incoming_file.fid; |
||
360 | new_file->file_length = eo_info->end_of_file; |
||
361 | new_file->flag_contains = contains; |
||
362 | new_file->free_chunk_list = NULL; |
||
363 | new_file->data_gathered = 0; |
||
364 | new_file->is_out_of_memory = FALSE; |
||
365 | entry->pkt_num = pinfo->num; |
||
366 | |||
367 | entry->hostname=g_filename_display_name(g_strcanon(eo_info->hostname,LEGAL_FILENAME_CHARS,'?')); |
||
368 | entry->filename=g_filename_display_name(g_strcanon(eo_info->filename,LEGAL_FILENAME_CHARS,'?')); |
||
369 | |||
370 | /* Insert the first chunk in the chunk list of this file */ |
||
371 | if (is_supported_filetype) { |
||
372 | insert_chunk(new_file, entry, eo_info); |
||
373 | } |
||
374 | |||
375 | if (new_file->is_out_of_memory) { |
||
376 | entry->content_type = |
||
377 | g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]", |
||
378 | aux_smb_fid_type_string, |
||
379 | new_file->data_gathered, |
||
380 | new_file->file_length, |
||
381 | try_val_to_str(contains, smb_eo_contains_string)); |
||
382 | } else { |
||
383 | if (new_file->file_length > 0) { |
||
384 | percent = (gfloat) (100*new_file->data_gathered/new_file->file_length); |
||
385 | } else { |
||
386 | percent = 0.0f; |
||
387 | } |
||
388 | |||
389 | entry->content_type = |
||
390 | g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]", |
||
391 | aux_smb_fid_type_string, |
||
392 | new_file->data_gathered, |
||
393 | new_file->file_length, |
||
394 | try_val_to_str(contains, smb_eo_contains_string), |
||
395 | percent); |
||
396 | } |
||
397 | |||
398 | object_list_add_entry(object_list, entry); |
||
399 | GSL_active_files = g_slist_append(GSL_active_files, new_file); |
||
400 | } |
||
401 | else if (is_supported_filetype) { |
||
402 | current_file = (active_file *)g_slist_nth_data(GSL_active_files, active_row); |
||
403 | /* Recalculate the current file flags */ |
||
404 | current_file->flag_contains = current_file->flag_contains|contains; |
||
405 | current_entry = object_list_get_entry(object_list, active_row); |
||
406 | |||
407 | insert_chunk(current_file, current_entry, eo_info); |
||
408 | |||
409 | /* Modify the current_entry object_type string */ |
||
410 | if (current_file->is_out_of_memory) { |
||
411 | current_entry->content_type = |
||
412 | g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]", |
||
413 | aux_smb_fid_type_string, |
||
414 | current_file->data_gathered, |
||
415 | current_file->file_length, |
||
416 | try_val_to_str(current_file->flag_contains, smb_eo_contains_string)); |
||
417 | } else { |
||
418 | percent = (gfloat) (100*current_file->data_gathered/current_file->file_length); |
||
419 | current_entry->content_type = |
||
420 | g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]", |
||
421 | aux_smb_fid_type_string, |
||
422 | current_file->data_gathered, |
||
423 | current_file->file_length, |
||
424 | try_val_to_str(current_file->flag_contains, smb_eo_contains_string), |
||
425 | percent); |
||
426 | } |
||
427 | } |
||
428 | |||
429 | return TRUE; /* State changed - window should be redrawn */ |
||
430 | } |
||
431 | |||
432 | |||
433 | /* This is the eo_protocoldata_reset function that is used in the export_object module |
||
434 | to cleanup any previous private data of the export object functionality before perform |
||
435 | the eo_reset function or when the window closes */ |
||
436 | void |
||
437 | eo_smb_cleanup(void) |
||
438 | { |
||
439 | int i, last; |
||
440 | active_file *in_list_file; |
||
441 | |||
442 | /* Free any previous data structures used in previous invocation to the |
||
443 | export_object_smb function */ |
||
444 | last = g_slist_length(GSL_active_files); |
||
445 | if (GSL_active_files) { |
||
446 | for (i=last-1; i>=0; i--) { |
||
447 | in_list_file = (active_file *)g_slist_nth_data(GSL_active_files, i); |
||
448 | if (in_list_file->free_chunk_list) { |
||
449 | g_slist_free(in_list_file->free_chunk_list); |
||
450 | in_list_file->free_chunk_list = NULL; |
||
451 | } |
||
452 | g_free(in_list_file); |
||
453 | } |
||
454 | g_slist_free(GSL_active_files); |
||
455 | GSL_active_files = NULL; |
||
456 | } |
||
457 | } |
||
458 | |||
459 | /* |
||
460 | * Editor modelines |
||
461 | * |
||
462 | * Local Variables: |
||
463 | * c-basic-offset: 4 |
||
464 | * tab-width: 8 |
||
465 | * indent-tabs-mode: nil |
||
466 | * End: |
||
467 | * |
||
468 | * ex: set shiftwidth=4 tabstop=8 expandtab: |
||
469 | * :indentSize=4:tabSize=8:noTabs=true: |
||
470 | */ |