root/branch/NetworkInventory/umitCore/SearchResult.py @ 3874

Revision 3874, 14.0 kB (checked in by gpolo, 4 years ago)

Removed unecessary imports to os.path

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