root/trunk/umit/core/SearchResult.py @ 5133

Revision 5133, 13.7 kB (checked in by luis, 4 years ago)

Fixing Search Window. It did not allow search by ports, and service product. It also had problems searching on OS Classes

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2005-2006 Insecure.Com LLC.
5# Copyright (C) 2007-2008 Adriano Monteiro Marques
6#
7# Author: Adriano Monteiro Marques <adriano@umitproject.org>
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23import os
24import os.path
25
26from glob import glob
27from fnmatch import fnmatch
28from tempfile import mktemp
29from types import StringTypes
30
31from umit.core.UmitDB import UmitDB
32from umit.core.NmapParser import NmapParser
33from umit.core.UmitLogging import log
34
35
36class SearchResult(object):   
37    def __init__(self):
38        """This method is called by classes that inherit this one at their
39        constructor methods. If in the future this method get some
40        functionallity, then, it will work fine for those classes that inherit
41        this one.
42        """
43        pass
44   
45    def search(self, **kargs):
46        log.debug(">>> Starting search process...")
47        parameters = ["keyword", "profile", "option", "target",
48                      "mac", "ipv4", "ipv6", "port", "service",
49                      "osclass", "osmatch", "product"]
50
51        # If nothing is passed, let's considerate that we want to search
52        # every port
53        self.port_closed = kargs.get("port_closed", True)
54        self.port_open = kargs.get("port_open", True)
55        self.port_filtered = kargs.get("port_filtered", True)
56
57
58        # Iterate over scan results searching for patterns
59        # Obs: This search looks for a result that matches each received
60        # parameters ("and" based search). If something fail, it
61        # desconsiderates the result
62       
63        keys = kargs.keys() # Catch given parameters names
64        log.debug(">>> Search parameters: %s" % keys)
65       
66        # Get parsed results, as NmapParser objects
67        for scan_result in self.get_scan_results():
68            self.parsed_scan = scan_result
69
70            # Test each given parameter against current parsed result
71            for parameter in parameters:
72                if parameter in keys:
73                    log.debug(">>> Searching '%s' at '%s'" % (parameter,
74                                                              kargs[parameter]))
75                   
76                    if not self.__getattribute__("match_%s" % parameter)\
77                       (kargs[parameter]):
78                        # A break here, means that there is no match for the
79                        # given pattern and, as it is an "and" based search, the
80                        # parsed result must be discarted
81                        log.debug(">>> Parsed result doesn't match patterns!")
82                        break
83            else:
84                log.debug(">>> Parsed result matches given patterns!")
85                yield self.parsed_scan
86
87        # If current scan result matches the pattern, yield the parsed object
88        # Else discart parsed result, and get another! ;-)
89
90    def get_scan_results(self):
91        # To be implemented by classes that are going to inherit this one
92        pass
93
94    def basic_match(self, keyword, property):
95        if keyword == "*" or keyword == "" or \
96           fnmatch(str(self.parsed_scan.__getattribute__(property)),
97                   "*%s*" % keyword):
98            return True # Pattern matches
99        return False # Pattern doesn't match
100
101    def match_keyword(self, keyword):
102        log.debug("Match keyword: %s" % keyword)
103        if self.match_profile(keyword) or \
104           self.match_option(keyword) or \
105           self.match_target(keyword) or \
106           self.match_mac(keyword) or \
107           self.match_ipv4(keyword) or \
108           self.match_ipv6(keyword) or \
109           self.match_service(keyword) or \
110           self.match_osmatch(keyword) or \
111           self.match_product(keyword) or \
112           self.basic_match(keyword, "nmap_output") or \
113           self.basic_match(keyword, "profile_name"):
114            return True
115
116    def match_profile(self, profile):
117        log.debug("Match profile: %s" % profile)
118        log.debug("Comparing: %s == %s ??" % \
119                  (str(self.parsed_scan.profile_name).lower(),
120                   "*%s*" % profile.lower()))
121        if profile == "*" or profile == "" or \
122           fnmatch(str(self.parsed_scan.profile_name).lower(),
123                   "*%s*" % profile.lower()):
124            return True # Pattern matches
125
126        return False # Pattern doesn't match
127
128        # FIXME: What is this?? I have commented this line, because it was
129        # useless Though, I don't have the time now to find out what is going
130        # on here but I'll do that later
131        #return self.basic_match(profile, "profile_name")
132   
133    def match_option(self, option):
134        log.debug("Match option: %s" % option)
135        return self.basic_match(option, "profile_options")
136
137    def match_target(self, target):
138        log.debug("Match target: %s" % target)
139        return self.basic_match(target, "target") or\
140               self.basic_match(target, "nmap_command")
141
142    def match_mac(self, mac):
143        log.debug("Match mac: %s" % mac)
144        return self.basic_match(mac, "mac")
145
146    def match_ipv4(self, ipv4):
147        log.debug("Match IPv4: %s" % ipv4)
148        return self.basic_match(ipv4, "ipv4")
149
150    def match_ipv6(self, ipv6):
151        log.debug("Match IPv6: %s" % ipv6)
152        return self.basic_match(ipv6, "ipv6")
153
154    def match_port(self, port):
155        log.debug("Match port:%s" % port)
156        if port == [""] or port == ["*"]:
157            return True
158       
159        ports = []
160       
161        for p in self.parsed_scan.ports:
162            for portid in p:
163                if self.port_open and portid["state"] == "open":
164                    ports.append(portid["portid"])
165                elif self.port_filtered and portid["state"] == "filtered":
166                    ports.append(portid["portid"])
167                elif self.port_closed and portid["state"] == "closed":
168                    ports.append(portid["portid"])
169                elif not self.port_open and \
170                        not self.port_filtered and \
171                        not self.port_closed:
172                    # In case every port state is False, add every port
173                    ports.append(portid["portid"])
174                   
175        for keyport in port:
176            if keyport not in ports:
177                return False # No match for asked port
178        else:
179            return True # Every given port matched current result
180
181    def match_service(self, service):
182        log.debug("Match service: %s" % service)
183        if service == "" or service == "*":
184            return True
185       
186        services = []
187        for port in self.parsed_scan.ports:
188            if 'name' in port and port['name'] not in services:
189                services.append(port["service_name"])
190
191        if service in services:
192            return True # Given service name matched current result
193        return False # Given service name didn't match current result
194
195    def match_osclass(self, osclass):
196        log.debug("Match osclass: %s" % osclass)
197        if osclass == "" or osclass == "*":
198            return True
199
200        class_info = self.split_osclass(osclass)
201        log.debug("Class info: %s" % class_info)
202       
203        for host in self.parsed_scan.hosts:
204            for oc in host.osclass:
205                #log.debug("Vendor: %s" % oc.get("vendor", ""))
206                #log.debug("OS Family: %s" % oc.get("osfamily", ""))
207                #log.debug("OS Gen: %s" % oc.get("osgen", ""))
208                #log.debug("Type: %s" % oc.get("type", ""))
209               
210                if oc.get("vendor", "").lower() == class_info[0] and \
211                       oc.get("osfamily", "").lower() == class_info[1] and \
212                       oc.get("osgen", "").lower() == class_info[2] and \
213                       oc.get("type", "").lower() == class_info[3]:
214                    return True # Found a match
215        return False
216
217    def match_osmatch(self, osmatch):
218        # XXX only the last osmatch is being used
219        log.debug("Match osmatch: %s" % osmatch)
220        if osmatch == "" or osmatch == "*":
221            return True
222
223        for host in self.parsed_scan.hosts:
224            if not host.osmatch:
225                continue
226            match = host.osmatch[-1].get("name", False)
227            if match and fnmatch(match.lower(), "*%s*" % osmatch.lower()):
228                return True
229        return False
230
231    def match_product(self, product):
232        log.debug("Match product: %s" % product)
233        if product == "" or product == "*":
234            return True
235       
236        products = []
237        for first in self.parsed_scan.ports:
238            for ports in first:
239                if fnmatch(ports.get("product", "").lower(),
240                           "*%s*" % product.lower()):
241
242                    # Given service product matched current result
243                    return True
244
245        # Given service product didn't match current result
246        return False
247
248    def split_osclass(self, osclass):
249        return [i.strip().lower() for i in osclass.split("|")]
250
251class SearchDB(SearchResult, object):
252    def __init__(self):
253        SearchResult.__init__(self)
254        log.debug(">>> SearchDB initialized")
255
256    def get_scan_results(self):
257        log.debug(">>> Getting scan results stored in data base")
258        u = UmitDB()
259
260        for scan in u.get_scans():
261            log.debug(">>> Retrieving result of scans_id %s" % scan.scans_id)
262            log.debug(">>> Nmap xml output: %s" % scan.nmap_xml_output)
263           
264            temp_file = mktemp(".usr", "umit_")
265           
266            tmp = open(temp_file, "w")
267            tmp.write(scan.nmap_xml_output)
268            tmp.close()
269
270            try:
271                parsed = NmapParser()
272                parsed.set_xml_file(temp_file)
273                parsed.parse()
274               
275                # Remove temporary file reference
276                parsed.nmap_xml_file = ""
277            except:
278                pass
279            else:
280                yield parsed
281
282class SearchDir(SearchResult, object):
283    def __init__(self, search_directory, file_extensions=["usr"]):
284        SearchResult.__init__(self)
285        log.debug(">>> SearchDir initialized")
286        self.search_directory = search_directory
287
288        if type(file_extensions) in StringTypes:
289            self.file_extensions = file_extensions.split(";")
290        elif type(file_extensions) == type([]):
291            self.file_extensions = file_extensions
292        else:
293            raise Exception("Wrong file extension format! '%s'" %
294                            file_extensions)
295
296    def get_scan_results(self):
297        log.debug(">>> Getting directory's scan results")
298        files = []
299        for ext in self.file_extensions:
300            files += glob(os.path.join(self.search_directory, "*.%s" % ext))
301
302        log.debug(">>> Scan results at selected directory: %s" % files)
303        for scan_file in files:
304            log.debug(">>> Retrieving scan result %s" % scan_file)
305            if os.access(scan_file, os.R_OK) and os.path.isfile(scan_file):
306
307                try:
308                    parsed = NmapParser()
309                    parsed.set_xml_file(scan_file)
310                    parsed.parse()
311                except:
312                    pass
313                else:
314                    yield parsed
315
316class SearchTabs(SearchResult, object):
317    def __init__(self, notebook):
318        self.scan_notebook = notebook
319
320    def get_scan_results(self):
321        scan_file = None
322        for i in range(self.scan_notebook.get_n_pages()):
323            sbook_page = self.scan_notebook.get_nth_page(i)
324
325            if not sbook_page.status.get_empty():
326                scan_file = sbook_page.parsed.nmap_xml_file
327                if hasattr(scan_file, "name"):
328                    # this scan was loaded from a file so nmap_xml_file is
329                    # actually a file object, but we are interested only in
330                    # the file name.
331                    scan_file = scan_file.name
332
333            if scan_file and os.access(scan_file, os.R_OK) and\
334               os.path.isfile(scan_file):
335                log.debug(">>> Retrieving unsaved scan result: %s" % scan_file)
336
337                try:
338                    parsed = NmapParser()
339                    parsed.set_xml_file(scan_file)
340                    parsed.parse()
341                    parsed.scan_name = "Unsaved " + sbook_page.get_tab_label()
342                    parsed.unsaved = True
343                except:
344                    pass
345                else:
346                    yield parsed
347
348if __name__ == "__main__":
349    s = SearchDir("/home/adriano/umit/test", ["usr", "xml"])
350    for result in s.search(\
351        keyword="",
352        #profile="",
353        #option="",
354        #started="1121737119",
355        #finished="1121737192",
356        #target="10.0.0.100-180",
357        #mac=":",
358        #ipv4="10.0.0.150",
359        #ipv6="",
360        #uptime=209980,
361        # lastboot="", MUST BE REMOVED FROM THE UI!
362        #port=["22", "80"],
363        #port_open="",
364        #port_filtered="",
365        #port_closed="",
366        #service="",
367        #osclass="Microsoft | Windows | 95/98/ME | General Purpose",
368        #osmatch="gentoo",
369        #product="Apache"\
370        ):
371
372        print "Ports:", result.hosts[-1].ports
373
Note: See TracBrowser for help on using the browser.