root/branch/PreferencesWindow/umitGUI/ScanPage.py @ 3864

Revision 3864, 45.1 kB (checked in by luis, 4 years ago)

Added missing files

Line 
1# Copyright (C) 2008 Adriano Monteiro Marques.
2#
3# Author: Adriano Monteiro Marques <adriano@umitproject.org>
4#         Luis A. Bastiao Silva <luis.kop@gmail.com>
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20import re
21import gtk
22import gobject
23
24from higwidgets.higboxes import HIGVBox
25from higwidgets.hignotebooks import HIGNotebook
26from higwidgets.higscrollers import HIGScrolledWindow
27
28from umitCore.UmitConf import CommandProfile, ProfileNotFound, is_maemo
29from umitCore.NmapParser import NmapParser
30
31from umitCore.NmapCommand import NmapCommand
32
33from umitGUI.ScanHostDetailsPage import ScanHostDetailsPage
34from umitGUI.ScanHostsView import ScanHostsView, SCANNING
35from umitGUI.ScanOpenPortsPage import ScanOpenPortsPage
36from umitGUI.ScanRunDetailsPage import ScanRunDetailsPage
37from umitGUI.ScanNmapOutputPage import ScanNmapOutputPage
38
39from umitGUI.ScanToolbar import ScanCommandToolbar, ScanToolbar
40
41from umitGUI.Icons import get_os_icon, get_os_logo, get_vulnerability_logo
42
43
44
45from umitPreferences.conf.ExposeConf import expose_conf
46
47from umitCore.Paths import Path
48from umitCore.UmitLogging import log
49from umitCore.I18N import _
50
51from types import StringTypes
52
53
54
55icon_dir = Path.pixmaps_dir
56
57
58
59
60
61
62class PageStatus(object):
63    """
64    Pages status:
65    The page status can be one of the following:
66       * saved: there is nothing to be saved in the current scan tab
67       * unsaved_unchanged: for recently scanned results that were parsed and
68                            is unchanged.
69       * unsaved_changed: for recently scanned results that were parsed and
70                          got some changes on its contents (like a comment)
71       * loaded_unchanged: for scan results that were loaded from a file and
72                           got no modifications
73       * loaded_changed: for scan results that were loaded from a file and
74                         got some modifications (like comments)
75       * parsing_result: the result is been parsed to be displayed at the tab
76       * scanning: there is no parsed result related to this tab, but there
77                   is a related scan running to show results on that tab
78       * empty: there is nothing related to this tab. His widgets are
79                disabled and this is the initial state of a new tab
80       * unknown: the page status is unknown
81       * scan_failed: the scan has failed
82       * search_loaded: the scan was loaded from a search result
83    """
84    def __init__(self, status=False):
85        if status:
86            self.status = status
87        else:
88            self.set_unknown()
89
90    def set_empty(self):
91        self._status = "empty"
92
93    def set_saved(self):
94        self._status = "saved"
95
96    def set_unsaved_unchanged(self):
97        self._status = "unsaved_unchanged"
98
99    def set_unsaved_changed(self):
100        self._status = "unsaved_changed"
101
102    def set_loaded_unchanged(self):
103        self._status = "loaded_unchanged"
104
105    def set_loaded_changed(self):
106        self._status = "loaded_changed"
107
108    def set_parsing_result(self):
109        self._status = "parsing_result"
110
111    def set_scanning(self):
112        self._status = "scanning"
113
114    def set_unknown(self):
115        self._status = "unknown"
116
117    def set_scan_failed(self):
118        self._status = "scan_failed"
119
120    def set_search_loaded(self):
121        self._status = "search_loaded"
122
123    def get_status(self):
124        return self._status
125
126    def set_status(self, status):
127        if status in self.available_status:
128            self._status = status
129        else:
130            raise Exception("Unknown status!")
131
132    def _verify_status(self, status):
133        if self._status == status:
134            return True
135        return False
136
137    def get_unsaved_unchanged(self):
138        return self._verify_status("unsaved_unchanged")
139
140    def get_unsaved_changed(self):
141        return self._verify_status("unsaved_changed")
142
143    def get_loaded_unchanged(self):
144        return self._verify_status("loaded_unchanged")
145
146    def get_loaded_changed(self):
147        return self._verify_status("loaded_changed")
148
149    def get_parsing_result(self):
150        return self._verify_status("parsing_result")
151
152    def get_scanning(self):
153        return self._verify_status("scanning")
154
155    def get_empty(self):
156        return self._verify_status("empty")
157
158    def get_unknown(self):
159        return self._verify_status("unknown")
160
161    def get_saved(self):
162        return self._verify_status("saved")
163
164    def get_scan_failed(self):
165        return self._verify_status("scan_failed")
166
167    def get_search_loaded(self):
168        return self._verify_status("search_loaded")
169
170
171    status = property(get_status, set_status)
172    saved = property(get_saved)
173    unsaved_unchanged = property(get_unsaved_unchanged)
174    unsaved_changed = property(get_unsaved_changed)
175    loaded_unchanged = property(get_loaded_unchanged)
176    loaded_changed = property(get_loaded_changed)
177    parsing_result = property(get_parsing_result)
178    scanning = property(get_scanning)
179    empty = property(get_empty)
180    unknown = property(get_unknown)
181    scan_failed = property(get_scan_failed)
182    search_loaded = property(get_search_loaded)
183   
184    _status = "unknown"
185    available_status = ["saved",
186                        "unsaved_unchanged",
187                        "unsaved_changed",
188                        "loaded_unchanged",
189                        "loaded_changed",
190                        "parsing_result",
191                        "scanning",
192                        "empty",
193                        "unknown",
194                        "search_loaded"]
195
196
197class ScanPage(HIGVBox):
198    def __init__(self):
199        HIGVBox.__init__(self)
200
201        # The borders are consuming too much space on Maemo. Setting it to
202        # 0 pixels while on Maemo
203        if is_maemo():
204            self.set_border_width(0)
205       
206        self.set_spacing(0)
207        self.status = PageStatus()
208        self.status.set_empty()
209       
210        self.changes = False
211        self.comments = {}
212
213        self.parsed = NmapParser()
214        self.top_box = HIGVBox()
215       
216        self.__create_toolbar()
217        self.__create_command_toolbar()
218        self.__create_scan_result()
219        self.disable_widgets()
220       
221        self.saved = False
222        self.saved_filename = ''
223
224        self.top_box.set_border_width(6)
225        self.top_box.set_spacing(5)
226       
227        self.top_box._pack_noexpand_nofill(self.toolbar)
228        self.top_box._pack_noexpand_nofill(self.command_toolbar)
229       
230        self._pack_noexpand_nofill(self.top_box)
231        self._pack_expand_fill(self.scan_result)
232
233    def target_focus(self):
234        self.toolbar.target_entry.child.grab_focus()
235
236    def select_first_profile(self):
237        model = self.toolbar.profile_entry.get_model()
238        if not len(model): # no profiles
239            return
240        self.toolbar.profile_entry.child.set_text(model[0][0])
241
242    def verify_changes(self):
243        return self.__verify_comments_changes()
244
245    def go_to_host(self, host):
246        """Go to host line on nmap output result"""
247        result_nb = self.scan_result.scan_result_notebook
248        result_nb.nmap_output.nmap_output.go_to_host(host)
249
250    def __create_scan_result(self):
251        self.scan_result = ScanResult()
252
253    def __create_toolbar(self):
254        self.toolbar = ScanToolbar()
255        self.toolbar.scan_button.set_sensitive(False)
256        self.empty_target = _("<target>")
257       
258        self.toolbar.target_entry.changed_handler = self.toolbar.target_entry.\
259            connect('changed', self.refresh_command_target)
260        self.toolbar.profile_entry.connect('changed', self.refresh_command)
261
262        self.toolbar.scan_button.connect('clicked', self.start_scan_cb)
263   
264    def __create_command_toolbar(self):
265        self.command_toolbar = ScanCommandToolbar()
266        self.command_toolbar.command_entry.connect('activate',
267            lambda x: self.toolbar.scan_button.clicked())
268
269        # This variable says if the command at command entry was edited by user
270        self.command_edited = False
271
272
273        # When user clicks insite the command entry for edition
274        self.command_toolbar.command_entry.connect("focus-in-event", 
275            self.remember_command)
276
277        # When user gets out of the command entry after edition
278        self.command_toolbar.command_entry.connect("focus-out-event", 
279            self.check_command)
280
281    def remember_command(self, widget, extra=None):
282        # User is inside command entry, probably editing it...
283       
284        # Target may be empty
285        self.old_target = self.toolbar.target_entry.selected_target
286
287        if not self.old_target:
288            self.old_target = self.empty_target
289
290        self.old_full_command = self.command_toolbar.command_entry.get_text()
291        self.old_command = self.old_full_command.split(self.old_target)[0]
292       
293
294    def check_command(self, widget, extra=None):
295        # User has left command entry. Verify if something has changed!
296        new_command = self.command_toolbar.command
297
298    def disable_widgets(self):
299        self.scan_result.set_sensitive(False)
300   
301    def enable_widgets(self):
302        self.scan_result.set_sensitive(True)
303   
304    def refresh_command_target(self, widget):
305        log.debug(">>> Refresh Command Target")
306
307        profile = self.toolbar.selected_profile
308        log.debug(">>> Profile: %s" % profile)
309       
310        if profile != '':
311            target = self.toolbar.selected_target
312            if target == '':
313                self.toolbar.scan_button.set_sensitive(False)
314            else:
315                self.toolbar.scan_button.set_sensitive(True)
316
317            try:
318                cmd_profile = CommandProfile()
319                command = cmd_profile.get_command(profile) % target
320                del(cmd_profile)
321               
322                self.command_toolbar.command = command
323            except ProfileNotFound:
324                pass # Go without a profile
325            except TypeError:
326                pass # The target is empty...
327                #self.profile_not_found_dialog()
328   
329    def refresh_command(self, widget):
330        #log.debug(">>> Refresh Command")
331        profile = self.toolbar.selected_profile
332        target = self.toolbar.selected_target
333
334        #log.debug(">>> Profile: %s" % profile)
335        #log.debug(">>> Target: %s" % target)
336       
337        if target == '':
338            target = self.empty_target
339
340        try:
341            cmd_profile = CommandProfile()
342            command = cmd_profile.get_command(profile) % target
343            del(cmd_profile)
344           
345            # scan button must be enable if -iR or -iL options are passed
346            if command.find('-iR') != -1 or command.find('-iL') != -1:
347                self.toolbar.scan_button.set_sensitive(True)
348
349                # For these nmap options, target is unecessary.
350                # Removes unnecessary target from the command
351                command = command.replace(target,'').strip()
352            elif target != self.empty_target:
353                self.toolbar.scan_button.set_sensitive(True)
354            else:
355                self.toolbar.scan_button.set_sensitive(False)
356
357            self.command_toolbar.command = command
358        except ProfileNotFound:
359            pass
360            #self.profile_not_found_dialog()
361        except TypeError:
362            pass # That means that the command string convertion "%" didn't work
363
364    def profile_not_found_dialog(self):
365        warn_dialog = HIGAlertDialog(message_format=_("Profile not found!"),
366            secondary_text=_("The profile name you selected/typed "
367                "couldn't be found, and probably doesn't exist. "
368                "Please, check the profile name and try again."),
369                type=gtk.MESSAGE_QUESTION)
370        warn_dialog.run()
371        warn_dialog.destroy()
372
373
374    def start_scan_cb(self, widget=None):
375        if not self.toolbar.scan_button.get_property("sensitive"):
376            return
377
378        target = self.toolbar.selected_target
379        command = self.command_toolbar.command
380        profile = self.toolbar.selected_profile
381
382        log.debug(">>> Start Scan:")
383        log.debug(">>> Target: '%s'" % target)
384        log.debug(">>> Profile: '%s'" % profile)
385        log.debug(">>> Command: '%s'" % command)
386        if expose_conf.page_inside:
387            if target and profile:
388                self.set_tab_label("%s on %s" %(profile, target))
389            elif target:
390                self.set_tab_label("Scan on %s" % target)
391            elif profile:
392                self.set_tab_label(profile)
393
394        if target != '':
395            self.toolbar.add_new_target(target)
396           
397            # TODO: Fix this workarround. The following line will set back the
398            # correct command to be executed after the refresh_command_target
399            # method that will be called by the targetcombo update method.
400            self.command_toolbar.command = command
401
402        if (command.find("-iR") == -1 and command.find("-iL") == -1):
403            if command.find("<target>") > 0:
404                warn_dialog = HIGAlertDialog(
405                    message_format=_("No Target Host!"), 
406                    secondary_text=_("Target specification is mandatory. "
407                        "Either by an address in the target input box or "
408                        "through the '-iR' and '-iL' nmap options. "
409                        "Aborting scan."), type=gtk.MESSAGE_ERROR)
410                warn_dialog.run()
411                warn_dialog.destroy()
412                return
413
414        if command != '':
415            # Setting status to scanning
416            self.status.set_scanning()
417            self.execute_command(command)
418        else:
419            warn_dialog = HIGAlertDialog(
420                message_format=_("Empty Nmap Command!"),
421                secondary_text=_("There is no command to execute! "
422                    "Maybe the selected/typed profile doesn't exist. "
423                    "Please, check the profile name or type the nmap "
424                    "command you would like to execute."),
425                type=gtk.MESSAGE_ERROR)
426            warn_dialog.run()
427            warn_dialog.destroy()
428
429    def close_tab(self):
430        try:
431            gobject.source_remove(self.verify_thread_timeout_id)
432        except:
433            pass
434
435    def collect_umit_info(self):
436        profile = CommandProfile()
437        profile_name = self.toolbar.selected_profile
438       
439        self.parsed.target = self.toolbar.get_target()
440        self.parsed.profile_name = profile_name
441        self.parsed.nmap_command = self.command_toolbar.get_command()
442        self.parsed.profile = profile.get_command(profile_name)
443        self.parsed.profile_hint = profile.get_hint(profile_name)
444        self.parsed.profile_description = profile.get_description(profile_name)
445        self.parsed.profile_annotation = profile.get_annotation(profile_name)
446        self.parsed.profile_options = profile.get_options(profile_name)
447
448        del(profile) # XXX not needed, will remove on next commit if no one
449                     #     complains.
450
451        if hasattr(self, "command_execution"):
452            self.parsed.nmap_output = self.command_execution.get_raw_output()
453        elif not self.parsed.nmap_output:
454            self.parsed.nmap_output = self.scan_result.get_nmap_output()
455
456    def kill_scan(self):
457        try:
458            self.command_execution.kill()
459        except AttributeError:
460            pass
461
462        self.scan_result.clear_nmap_output()
463        self.scan_result.clear_host_view()
464        self.status.set_empty()
465        self.disable_widgets()
466   
467    def execute_command(self, command):
468        log.critical("execute_command %s" % command)
469        try:
470            alive = self.command_execution.scan_state()
471            if alive:
472                warn_dialog = HIGAlertDialog(
473                    message_format=_("Scan has not finished yet"),
474                    secondary_text=_("Another scan is running in "
475                        "the background. To start another scan and kill "
476                        "the old one, click Ok. To wait for the "
477                        "conclusion of the old scan, choose Cancel."),
478                    type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK_CANCEL)
479                response = warn_dialog.run()
480                warn_dialog.destroy()
481
482                if response == gtk.RESPONSE_OK:
483                    # Kill current scan, and let the another one to be created
484                    self.kill_scan()
485                else:
486                    return
487        except:
488            pass
489
490        self.command_execution = NmapCommand(command)
491       
492        try:
493            self.command_execution.run_scan()
494        except Exception, msg:
495            warn_dialog = HIGAlertDialog(
496                message_format=_("Command is missing!"),
497                secondary_text=_("It seems that your profile's command "
498                    "is missing or something else went wrong. Please, "
499                    "try to remove and recreate your profile."),
500                type=gtk.MESSAGE_ERROR)
501            warn_dialog.run()
502            warn_dialog.destroy()
503
504        # Ask NmapOutputViewer to show/refresh nmap output from given file
505        self.scan_result.show_nmap_output(\
506            self.command_execution.get_output_file())
507
508        # Set a "EXECUTING" icon to host list
509        self.scan_result.set_hosts({SCANNING: {'stock': gtk.STOCK_EXECUTE,
510            'action': None}})
511        self.scan_result.set_services({SCANNING:{'action': None}})
512
513        # Clear port list, to remove old information
514        self.scan_result.clear_port_list()
515
516        # When scan starts, change to nmap output view tab and refresh output
517        self.scan_result.change_to_nmap_output_tab()
518        self.scan_result.refresh_nmap_output()
519       
520        self.enable_widgets()
521
522        # Add a timeout function
523        self.verify_thread_timeout_id = gobject.timeout_add(2000, 
524            self.verify_execution)
525
526    def verify_execution(self):
527        # Using new subprocess style
528        try:
529            alive = self.command_execution.scan_state()
530        except:
531            self.disable_widgets()
532            self.status.set_scan_failed()
533            self.scan_result.set_nmap_output(self.command_execution.get_error())
534            return False
535
536        # Maybe this automatic refresh should be eliminated
537        # to avoid processor burning
538        self.scan_result.refresh_nmap_output()
539       
540        if alive:
541            return True
542        else:
543            self.parse_result(self.command_execution.get_xml_output_file())
544            return False
545
546    def load_result(self, file_to_parse):
547        ####
548        # Setting status to parsing_result
549        self.status.set_parsing_result()
550        ####
551        self._parse(file_to_parse=file_to_parse)
552
553        ####
554        # Setting status to loaded_unchanged
555        self.status.set_loaded_unchanged()
556        ####
557
558    def parse_result(self, file_to_parse):
559        ####
560        # Setting status to parsing_result
561        self.status.set_parsing_result()
562        ####
563        self._parse(file_to_parse=file_to_parse)
564
565        ####
566        # Setting status to unsaved_unchanged
567        self.status.set_unsaved_unchanged()
568        ####
569
570    def load_from_parsed_result(self, parsed_result):
571        ####
572        # Setting status to parsing_result
573        self.status.set_parsing_result()
574        ####
575        self._parse(parsed_result=parsed_result)
576
577        ####
578        # Setting status to unsaved_unchanged
579        self.status.set_unsaved_unchanged()
580        ####
581
582    def _parse(self, file_to_parse=None, parsed_result=None):
583        """Called when scan is done. Verify if any host were found."""
584        log.debug(">>> XML output file that is going to be "
585            "parsed: %s" % file_to_parse)
586       
587        self.host_view_selection = self.scan_result.get_host_selection()
588        self.service_view_selection = self.scan_result.get_service_selection()
589       
590        # All hosts details pages
591        self.host_pages = []
592        self.changes = True
593       
594        self.host_view_selection.connect('changed', self.update_host_info)
595        self.service_view_selection.connect('changed', self.update_service_info)
596        self.scan_result.scan_host_view.clear_host_list()
597        self.hosts = {}
598        self.services = {}
599
600        # Removed and created again to avoid host duplication problems when
601        # making multiple scans inside the same scan tab
602        try: del(self.parsed) 
603        except: pass
604
605        if file_to_parse:
606            self.parsed = NmapParser()
607            self.parsed.set_xml_file(file_to_parse)
608            try:
609                log.debug(">>> Start parsing...")
610                self.parsed.parse()
611                log.debug(">>> Successfully parsed!")
612            except:
613                log.debug(">>> An exception occourried during xml ouput "
614                    "parsing")
615                try:
616                    error = self.command_execution.get_error()
617                except AttributeError:
618                    error = _("Couldn't retrieve the error raised by "
619                        "the command!")
620                except:
621                    error = _("Unknown error!")
622
623                log.debug(">>> Error: '%s'" % error)
624
625                # Treat root exceptions more carefully!
626                if re.findall('[rR][oO0]{2}[tT]', error):
627                    need_root = HIGAlertDialog(
628                            message_format=_('Root privileges are needed!'),
629                            secondary_text=error)
630                    need_root.run()
631                    need_root.destroy()
632                else:
633                    unknown_problem = HIGAlertDialog(
634                        message_format=_('An unexpected error occourried!'),
635                        secondary_text=error)
636                    unknown_problem.run()
637                    unknown_problem.destroy()
638                return
639        elif parsed_result:
640            self.parsed = parsed_result
641
642        if int(self.parsed.get_hosts_up()):
643            for host in self.parsed.get_hosts():
644                hostname = host.get_hostname()
645                host_page = self.set_host_details(host)
646                list_states = ["open", "filtered", "open|filtered"]
647
648                for service in host.services:
649                    name = service["service_name"]
650                    state = service["port_state"]
651
652                    if state not in list_states:
653                        continue
654                   
655                    if name not in self.services.keys():
656                        self.services[name] = {"hosts":[]}
657
658                    hs = {"host": host, "page": host_page, "hostname": hostname}
659                    hs.update(service)
660                       
661                    self.services[name]["hosts"].append(hs)
662                   
663                self.hosts[hostname] = {'host': host, 'page': host_page}
664                   
665                host_details = self.hosts[hostname]['page'].host_details
666                host_info = self.hosts[hostname]['host']
667                   
668                try:
669                    host_details.set_os_image(
670                        get_os_logo(host.get_osmatch()['name']))
671                except:
672                    host_details.set_os_image(get_os_logo(''))
673                   
674                host_details.set_vulnerability_image(get_vulnerability_logo(
675                    host_info.get_open_ports()))
676                   
677                icon = None
678                try:icon = get_os_icon(host.get_osmatch()['name'])
679                except:icon = get_os_icon('')
680                   
681                self.scan_result.scan_host_view.add_host(
682                    {hostname: {'stock': icon, 'action':None}})
683               
684            # Select the first host found
685            self.host_view_selection.select_iter(
686                self.scan_result.scan_host_view.host_list.get_iter_root())
687
688        self.scan_result.scan_host_view.set_services(self.services.keys())
689           
690        try:
691            # And them, we update the nmap output! ;)
692            self.scan_result.scan_result_notebook.nmap_output.\
693                nmap_output.refresh_output()
694        except:
695            # Put saved nmap output
696            self.scan_result.scan_result_notebook.nmap_output.\
697                        nmap_output.text_buffer.\
698                        set_text('\n'.join(self.parsed.get_nmap_output().\
699                                           split('\\n')))
700           
701        target = self.parsed.get_target()
702           
703        if target != '':
704            self.toolbar.target_entry.child.set_text(target)
705           
706        profile_name = self.parsed.profile_name
707           
708        if profile_name != '':
709            profile = CommandProfile()
710            profile.add_profile(self.parsed.profile_name,
711                                command=self.parsed.profile,
712                                hint=self.parsed.profile_hint,
713                                options=self.parsed.profile_options,
714                                description=self.parsed.profile_description,
715                                annotation=self.parsed.profile_annotation)
716            del(profile)
717               
718            self.toolbar.profile_entry.update()
719               
720            self.toolbar.selected_profile = profile_name
721        else:
722            pass
723            # The line bellow seens to be useless
724            #self.command_toolbar.command = self.parsed.get_nmap_command()
725
726        self.collect_umit_info()
727        self.switch_scan_details(self.__set_scan_info())
728        self.check_fingerprints()
729
730    def check_fingerprints(self):
731        re_host = re.compile(r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})")
732        re_os_fp = re.compile(r"(SInfo.*\);)")
733        re_service_fp = re.compile(r"(SF-Port.*\);)")
734        re_service_number = re.compile(r"SF-Port(\d+)")
735       
736        nmap_output = self.parsed.nmap_output.split("\\n\\n")
737        fingerprints = {}
738        current_ip = None
739
740        for line in nmap_output:
741            match_host = re_host.search(line)
742            match_os = re_os_fp.search(line)
743            match_service = re_service_fp.search(line)
744           
745            if match_host:
746                current_ip = match_host.groups()[0]
747            if match_os:
748                if current_ip not in fingerprints.keys():
749                    fingerprints[current_ip] = {}
750               
751                fingerprints[current_ip]["os"] = match_os.groups()[0]
752            if match_service:
753                if current_ip not in fingerprints.keys():
754                    fingerprints[current_ip] = {}
755
756                fp = match_service.groups()[0]
757               
758                fingerprints[current_ip]["service"] = fp
759
760                #port = re_service_port.search(fp).groups()[0]
761                #fingerprints[current_ip]["service_port"] = port
762                #fingerprints[current_ip]["service_name"]
763               
764
765        # In the future, umit is going to catch the fingerprint informations
766        # and load a umitGUI.OSFingerprintReport or
767        # umitGUI.ServiceFingerprintReport window that will let user
768        # to directely register a new fingerprint from the interface
769        # without worrying about moving his mouse off the interface.
770
771        """
772        for fp in fingerprints:
773            # We've found a new fp! Please contribute dialog
774            # If ok, show the form, sending the ip and fingerprint.
775        """
776
777        key_num = len(fingerprints.keys())
778        dialog_text = ("Umit has found that %s. The submission and "
779            "registration of fingerprints are very important for you "
780            "and the Nmap project! If you would like to contribute "
781            "to see your favorite network mapper recognizing those "
782            "fingerprints in the future, choose the Ok button, and a "
783            "submission page will be open in your default web browser "
784            "with instructions about how to proceed on this registration.")
785       
786        if key_num == 1:
787            msg = _("you network scan discovered an unknown fingerprint "
788                "sent by the host %s") % fingerprints.keys()[0]
789           
790            self.show_contribute_dialog(dialog_text % msg)
791
792        elif key_num > 1:
793            msg = _("your network scan discovered several unknown "
794                "fingerprints sent by the follwoing hosts: ")
795            for i in fingerprints:
796                msg += "%s, " % i
797            msg = msg[:-2]
798
799            self.show_contribute_dialog(dialog_text % msg)
800
801
802    def show_contribute_dialog(self, dialog_text):
803        contribute_dialog = HIGAlertDialog(
804            message_format=_("Unrecognized Services/OS Fingerprints Found!"),
805            secondary_text=dialog_text, type=gtk.MESSAGE_QUESTION,
806            buttons=gtk.BUTTONS_OK_CANCEL)
807        response = contribute_dialog.run()
808        contribute_dialog.destroy()
809
810        if response == gtk.RESPONSE_OK:
811            webbrowser.open("http://www.insecure.org/nmap/submit/")
812       
813
814    def __verify_comments_changes(self):
815        try:
816            for hostname in self.hosts:
817                host_details = self.hosts[hostname]['page'].host_details
818                if host_details.get_comment() != self.comments[hostname]:
819                    log.debug("Changes on comments")
820                    self.changes = True
821                    return True
822        except:
823            return False
824   
825    def __set_scan_info(self):
826        self.clean_scan_details()
827        run_details = ScanRunDetailsPage()
828       
829        run_details.set_command_info(
830            {'command': self.parsed.get_nmap_command(),
831             'version': self.parsed.get_scanner_version(),
832             'verbose': self.parsed.get_verbose_level(),
833             'debug': self.parsed.get_debugging_level()
834            })
835       
836        run_details.set_general_info(
837            {'start': self.parsed.get_formated_date(),
838             'finish': self.parsed.get_formated_finish_date(),
839             'hosts_up': str(self.parsed.get_hosts_up()),
840             'hosts_down': str(self.parsed.get_hosts_down()),
841             'hosts_scanned': str(self.parsed.get_hosts_scanned()),
842             'open_ports': str(self.parsed.get_open_ports()),
843             'filtered_ports': str(self.parsed.get_filtered_ports()),
844             'closed_ports': str(self.parsed.get_closed_ports())
845            })
846             
847        run_details.set_scan_infos(self.parsed.get_scaninfo())
848       
849        return run_details
850   
851    def update_host_info(self, widget):
852        self.scan_result.scan_result_notebook.port_mode()
853
854        model_host_list, selection = widget.get_selected_rows()
855        #host_objs = [self.hosts[model_host_list[i[0]][1]] for i in selection]
856        host_objs = []
857        for i in selection:
858            key = model_host_list[i[0]][1]
859            if self.hosts.has_key(key):
860                host_objs.append(self.hosts[key])
861
862        self.clean_host_details()
863       
864        if len(host_objs) == 1:
865            self.set_single_host_port(host_objs[0]['host'])
866            self.switch_host_details(host_objs[0]['page'])
867        else:
868            self.set_multiple_host_port(host_objs)
869            self.switch_host_details(self.set_multiple_host_details(host_objs))
870
871        # Switch nmap output to show first host occourrence
872        try:
873            self.go_to_host(host_objs[0]['host'].get_hostname())
874        except IndexError:
875            pass
876
877    def update_service_info(self, widget):
878        self.scan_result.scan_result_notebook.host_mode()
879       
880        model_service_list, selection = widget.get_selected_rows()
881        serv_objs = []
882        for i in selection:
883            key = model_service_list[i[0]][0]
884            if self.services.has_key(key):
885                serv_objs.append(self.services[key])
886
887        # Removing current widgets from the host details page
888        self.clean_host_details()
889
890        if len(serv_objs) == 1:
891            self.set_single_service_host(serv_objs[0]['hosts'])
892            self.switch_host_details([page["page"] \
893                                      for page in serv_objs[0]['hosts']])
894        else:
895            servs = []
896            for s in serv_objs:
897                servs.append({"service_name": s["hosts"][0]["service_name"],
898                     "hosts": s["hosts"]})
899
900            self.set_multiple_service_host(servs)
901           
902            pages = []
903            for serv in [serv["hosts"] for serv in serv_objs]:
904                for h in serv:
905                    # Prevent from adding a host more then once
906                    if h["page"] not in pages:
907                        pages.append(h["page"])
908           
909            self.switch_host_details(pages)
910
911        # Change scan tab to "Ports/Hosts"
912        self.scan_result.scan_result_notebook.set_current_page(0)
913   
914    def clean_host_details(self):
915        parent = self.scan_result.scan_result_notebook.host_details_vbox
916        children = parent.get_children()
917       
918        for child in children:
919            parent.remove(child)
920           
921    def clean_scan_details(self):
922        parent = self.scan_result.scan_result_notebook.scan_details_vbox
923        children = parent.get_children()
924       
925        for child in children:
926            parent.remove(child)
927
928    def switch_host_details(self, page):
929        if type(page) == type([]):
930            if len(page) > 1:
931                for p in page:
932                    p.hide()
933                    p.set_expanded(False)
934                    result_nb = self.scan_result.scan_result_notebook
935                    result_nb.host_details_vbox._pack_noexpand_nofill(p)
936               
937                result_nb = self.scan_result.scan_result_notebook
938                result_nb.host_details_vbox.show_all() 
939                return
940            elif len(page) == 1:
941                page = page[0]
942       
943        try:
944            page.hide()
945        except: # XXX except what ?
946            pass
947        else:
948            result_nb = self.scan_result.scan_result_notebook
949            result_nb.host_details_vbox._pack_noexpand_nofill(page)
950            page.set_expanded(True)
951            page.show_all()
952   
953    def switch_scan_details(self, page):
954        # Removing current widget from the host details page
955        result_nb = self.scan_result.scan_result_notebook
956        result_nb.scan_details_vbox._pack_noexpand_nofill(page)
957        page.show_all()
958   
959    def set_multiple_host_details(self, host_list):
960        hosts = []
961        for h in host_list:
962            hosts.append(h['page'])
963       
964        return hosts
965
966    def _save_comment(self, widget, extra, host_id):
967        if self.status.unsaved_unchanged:
968            self.status.set_unsaved_changed()
969        elif self.status.loaded_unchanged or self.status.saved:
970            self.status.set_loaded_changed()
971       
972        # Catch a comment and record it to be saved posteriorly
973        log.debug(">>> Catching edited comment to be saved posteriorly.")
974        buff = widget.get_buffer()
975        self.parsed.set_host_comment(host_id, 
976            buff.get_text(buff.get_start_iter(), buff.get_end_iter()))
977   
978    def set_host_details(self, host):
979        # Start connecting event to automatically update comments, target
980        # and profile infos
981        host_page = ScanHostDetailsPage(host.get_hostname())
982        host_details = host_page.host_details
983
984        log.debug(">>> Setting host details")
985        log.debug(">>> Hostname: %s" % host.get_hostname())
986        log.debug(">>> Comment: %s" % self.parsed.get_host_comment(host.id))
987        host_details.set_comment(self.parsed.get_host_comment(host.id))
988       
989        # Setting events to automatically record the commentary to be saved
990        host_page.host_details.comment_txt_vw.connect("insert-at-cursor", 
991            self._save_comment, host.id)
992        host_page.host_details.comment_txt_vw.connect("focus-out-event", 
993            self._save_comment, host.id)
994
995       
996        self.comments[host.get_hostname()] = host.get_comment()
997       
998        uptime = host.get_uptime()
999
1000        host_details.set_host_status({'state':host.get_state(),
1001                                      'open':str(host.get_open_ports()),
1002                                      'filtered':str(host.get_filtered_ports()),
1003                                      'closed':str(host.get_closed_ports()),
1004                                      'scanned':str(host.get_scanned_ports()),
1005                                      'uptime':uptime['seconds'],
1006                                      'lastboot':uptime['lastboot']})
1007
1008       
1009        ipv4 = host.get_ip().get('addr', '') 
1010        ipv6 = host.get_ipv6().get('addr', '')
1011        mac = host.get_mac().get('addr', '')
1012       
1013        host_details.set_addresses({'ipv4': ipv4, 'ipv6': ipv6, 'mac': mac}) 
1014        host_details.set_hostnames(host.get_hostnames())
1015       
1016        os = host.get_osmatch()
1017        if os:
1018            os['portsused'] = host.get_ports_used()
1019            os['osclass'] = host.get_osclasses()
1020       
1021        host_details.set_os(os)
1022        host_details.set_tcpseq(host.get_tcpsequence())
1023        host_details.set_ipseq(host.get_ipidsequence())
1024        host_details.set_tcptsseq(host.get_tcptssequence())
1025       
1026        return host_page
1027   
1028    def set_single_host_port(self, host):
1029        host_page = self.scan_result.scan_result_notebook.open_ports.host
1030        host_page.switch_port_to_list_store()
1031       
1032        p = host.get_ports()
1033        ports = []
1034        for port in p:
1035            ports += port['port']
1036       
1037        host_page.clear_port_list()
1038        for p in ports:
1039            host_page.add_port([self.findout_service_icon(p),
1040                                int(p.get('portid', '0')),
1041                                p.get('protocol', ''),
1042                                p.get('port_state', ''),
1043                                p.get('service_name', ''),
1044                                p.get('service_product', '')])
1045
1046    def set_single_service_host(self, service):
1047        host_page = self.scan_result.scan_result_notebook.open_ports.host
1048        host_page.switch_host_to_list_store()
1049        host_page.clear_host_list()
1050
1051        for h in service:
1052            host_page.add_host([self.findout_service_icon(h),
1053                                h.get('hostname', ''),
1054                                int(h.get('portid', '0')),
1055                                h.get('protocol', ''),
1056                                h.get('port_state', ''),
1057                                h.get('service_product', ''),
1058                                h.get('service_version', '')])
1059       
1060   
1061    def set_multiple_host_port(self, host_list):
1062        host_page = self.scan_result.scan_result_notebook.open_ports.host
1063        host_page.switch_port_to_tree_store()
1064        host_page.clear_port_tree()
1065       
1066        for host in host_list:
1067            parent = host_page.port_tree.append(None, 
1068                [host['host'].get_hostname(), '', 0, '', '', '', ''])
1069            for port in host['host'].get_ports():
1070                for p in port.get('port', []):
1071                    host_page.port_tree.append(parent,
1072                        ['', self.findout_service_icon(p),
1073                         int(p.get('portid', "0")),
1074                         p.get('protocol', ''),
1075                         p.get('port_state', ""),
1076                         p.get('service_name', _("Unknown")),
1077                         p.get('service_product', "")])
1078
1079    def set_multiple_service_host(self, service_list):
1080        host_page = self.scan_result.scan_result_notebook.open_ports.host
1081        host_page.switch_host_to_tree_store()
1082        host_page.clear_host_tree()
1083       
1084        for host in service_list:
1085            parent = host_page.host_tree.append(None, [host['service_name'],
1086                                                       '','',0,'','', '', ''])
1087            for h in host['hosts']:
1088                host_page.host_tree.append(parent, 
1089                    ['', self.findout_service_icon(h), h["hostname"],
1090                     int(h.get('portid', "0")), h.get('protocol', ""),
1091                     h.get('port_state', _("Unknown")),
1092                     h.get('service_product', ''), h.get('service_version', 
1093                     _("Unknown"))])
1094   
1095    def findout_service_icon(self, port_info):
1096        return gtk.STOCK_YES
1097
1098   
1099
1100class ScanResult(gtk.HPaned):
1101    def __init__(self):
1102        gtk.HPaned.__init__(self)
1103       
1104        self.scan_host_view = ScanHostsView()
1105        self.scan_result_notebook = ScanResultNotebook()
1106
1107        self.pack1(self.scan_host_view, True, False)
1108        self.pack2(self.scan_result_notebook, True, False)
1109
1110    def set_nmap_output(self, msg):
1111        nmap_output = self.scan_result_notebook.nmap_output.nmap_output
1112        nmap_output.text_view.get_buffer().set_text(msg)
1113        nmap_output.update_output_colors()
1114
1115    def clear_nmap_output(self):
1116        nmap_output = self.scan_result_notebook.nmap_output.nmap_output
1117        nmap_output.text_view.get_buffer().set_text("")
1118
1119    def clear_host_view(self):
1120        self.set_hosts({})
1121
1122    def clear_service_view(self):
1123        self.set_services({})
1124
1125    def get_host_selection(self):
1126        return self.scan_host_view.host_view.get_selection()
1127
1128    def get_service_selection(self):
1129        return self.scan_host_view.service_view.get_selection()
1130
1131    def get_nmap_output(self):
1132        return self.scan_result_notebook.nmap_output.get_nmap_output()
1133
1134    def show_nmap_output(self, file):
1135        """Ask NmapOutputViewer to show/refresh nmap output from given file."""
1136        self.scan_result_notebook.nmap_output.nmap_output.show_nmap_output(file)
1137
1138    def set_hosts(self, hosts_dic):
1139        """Set hosts at host list."""
1140        self.scan_host_view.set_hosts(hosts_dic)
1141
1142    def set_services(self, services_dic):
1143        self.scan_host_view.set_services(services_dic)
1144
1145    def clear_port_list(self):
1146        """Clear Umit's scan result ports list."""
1147        self.scan_result_notebook.open_ports.host.clear_port_list()
1148
1149    def change_to_nmap_output_tab(self):
1150        """Show the nmap output tab."""
1151        self.scan_result_notebook.set_current_page(1)
1152
1153    def refresh_nmap_output(self):
1154        """Refresh Nmap output in nmap output tab."""
1155        self.scan_result_notebook.nmap_output.nmap_output.refresh_output()
1156       
1157
1158
1159class ScanResultNotebook(HIGNotebook):
1160    def __init__(self):
1161        HIGNotebook.__init__(self)
1162        self.set_scrollable(True)
1163        self.set_border_width(5)
1164       
1165        self.__create_widgets()
1166        self.__nmap_output_refreshing()
1167       
1168        self.append_page(self.open_ports_page, gtk.Label(_('Ports / Hosts')))
1169        self.append_page(self.nmap_output_page, gtk.Label(_('Nmap Output')))
1170
1171        self.append_page(self.host_details_page, gtk.Label(_('Host Details')))
1172        self.append_page(self.scan_details_page, gtk.Label(_('Scan Details')))
1173
1174    def get_nmap_output(self):
1175        return self.nmap_output.get_map_output()
1176   
1177    def host_mode(self):
1178        self.open_ports.host.host_mode()
1179
1180    def port_mode(self):
1181        self.open_ports.host.port_mode()
1182   
1183    def __create_widgets(self):
1184        self.open_ports_page = HIGVBox()
1185        self.nmap_output_page = HIGVBox()
1186        self.host_details_page = HIGScrolledWindow()
1187        self.scan_details_page = HIGScrolledWindow()
1188        self.scan_details_vbox = HIGVBox()
1189        self.host_details_vbox = HIGVBox()
1190       
1191        self.open_ports = ScanOpenPortsPage()
1192        self.nmap_output = ScanNmapOutputPage()
1193       
1194        self.no_selected = gtk.Label(_('No host selected!'))
1195        self.host_details = self.no_selected
1196       
1197        self.no_details = gtk.Label(_('Scan is not finished yet!'))
1198        self.scan_details = self.no_details
1199       
1200        self.open_ports_page.add(self.open_ports)
1201        self.nmap_output_page.add(self.nmap_output)
1202       
1203        self.host_details_page.add_with_viewport(self.host_details_vbox)
1204        self.host_details_vbox._pack_expand_fill(self.host_details)
1205       
1206        self.scan_details_page.add_with_viewport(self.scan_details_vbox)
1207        self.scan_details_vbox._pack_expand_fill(self.scan_details)
1208   
1209    def __nmap_output_refreshing(self):
1210        self.connect('switch-page', self.refresh_cb)
1211   
1212    def refresh_cb(self, widget, page=None, page_num=None):
1213        if self.nmap_output.nmap_output.thread.isAlive():
1214            if page_num == 2:
1215                self.nmap_output.nmap_output.refresh_output(None)
1216
1217
1218if __name__ == "__main__":
1219    status = PageStatus("empty")
1220    status.set_saved()
1221    status.set_unsaved_unchanged()
1222    status.set_unsaved_changed()
1223    status.set_loaded_unchanged()
1224    status.set_loaded_changed()
1225    status.set_empty()
1226    status.set_scanning()
1227    status.set_parsing_result()
1228    status.set_unknown()
1229
1230    print "Saved:", status.saved
1231    print "Unsaved unchanged:", status.unsaved_unchanged
1232    print "Unsaved changed:", status.unsaved_changed
1233    print "Loaded unchanged:", status.loaded_unchanged
1234    print "Loaded changed:", status.loaded_changed
1235    print "Empty:", status.empty
1236    print "Scanning:", status.scanning
1237    print "Parsing result:", status.parsing_result
1238    print "Unknown:", status.unknown
1239
1240
Note: See TracBrowser for help on using the browser.