nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
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 */