summaryrefslogtreecommitdiffstats
path: root/contrib/check_nmap.py
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/check_nmap.py')
-rw-r--r--contrib/check_nmap.py441
1 files changed, 0 insertions, 441 deletions
diff --git a/contrib/check_nmap.py b/contrib/check_nmap.py
deleted file mode 100644
index 481a62bf..00000000
--- a/contrib/check_nmap.py
+++ /dev/null
@@ -1,441 +0,0 @@
1#!/usr/bin/python
2# Change the above line if python is somewhere else
3
4#
5# check_nmap
6#
7# Program: nmap plugin for Nagios
8# License: GPL
9# Copyright (c) 2000 Jacob Lundqvist (jaclu@galdrion.com)
10#
11_version_ = '1.21'
12#
13#
14# Description:
15#
16# Does a nmap scan, compares open ports to those given on command-line
17# Reports warning for closed that should be open and error for
18# open that should be closed.
19# If optional ports are given, no warning is given if they are closed
20# and they are included in the list of valid ports.
21#
22# Requirements:
23# python
24# nmap
25#
26# History
27# -------
28# 1.21 2004-07-23 rippeld@hillsboroughcounty.org Updated parsing of nmap output to correctly identify closed ports
29# 1.20 2000-07-15 jaclu Updated params to correctly comply to plugin-standard
30# moved support classes to utils.py
31# 1.16 2000-07-14 jaclu made options and return codes more compatible with
32# the plugin developer-guidelines
33# 1.15 2000-07-14 jaclu added random string to temp-file name
34# 1.14 2000-07-14 jaclu added check for error from subproc
35# 1.10 2000-07-14 jaclu converted main part to class
36# 1.08 2000-07-13 jaclu better param parsing
37# 1.07 2000-07-13 jaclu changed nmap param to -P0
38# 1.06 2000-07-13 jaclu make sure tmp file is deleted on errors
39# 1.05 2000-07-12 jaclu in debug mode, show exit code
40# 1.03 2000-07-12 jaclu error handling on nmap output
41# 1.01 2000-07-12 jaclu added license
42# 1.00 2000-07-12 jaclu implemented timeout handling
43# 0.20 2000-07-10 jaclu Initial release
44
45
46import sys, os, string, random
47
48import tempfile
49from getopt import getopt
50
51#
52# import generic Nagios-plugin stuff
53#
54import utils
55
56# Where temp files should be placed
57tempfile.tempdir='/usr/local/nagios/var'
58
59# Base name for tempfile
60tempfile.template='check_nmap_tmp.'
61
62# location and possibly params for nmap
63nmap_cmd='/usr/bin/nmap -P0'
64
65
66
67
68
69
70#
71# the class that does all the real work in this plugin...
72#
73#
74class CheckNmap:
75
76 # Retcodes, so we are compatible with nagios
77 #ERROR= -1
78 UNKNOWN= -1
79 OK= 0
80 WARNING= 1
81 CRITICAL= 2
82
83
84 def __init__(self,cmd_line=[]):
85 """Constructor.
86 arguments:
87 cmd_line: normaly sys.argv[1:] if called as standalone program
88 """
89 self.tmp_file=''
90 self.host='' # host to check
91 self.timeout=10
92 self.debug=0 # 1= show debug info
93 self.ports=[] # list of mandatory ports
94 self.opt_ports=[] # list of optional ports
95 self.ranges='' # port ranges for nmap
96 self.exit_code=0 # numerical exit-code
97 self.exit_msg='' # message to caller
98
99 self.ParseCmdLine(cmd_line)
100
101 def Run(self):
102 """Actually run the process.
103 This method should be called exactly once.
104 """
105
106 #
107 # Only call check_host if cmd line was accepted earlier
108 #
109 if self.exit_code==0:
110 self.CheckHost()
111
112 self.CleanUp()
113 return self.exit_code,self.exit_msg
114
115 def Version(self):
116 return 'check_nmap %s' % _version_
117
118 #-----------------------------------------
119 #
120 # class internal stuff below...
121 #
122 #-----------------------------------------
123
124 #
125 # Param checks
126 #
127 def param2int_list(self,s):
128 lst=string.split(string.replace(s,',',' '))
129 try:
130 for i in range(len(lst)):
131 lst[i]=int(lst[i])
132 except:
133 lst=[]
134 return lst
135
136 def ParseCmdLine(self,cmd_line):
137 try:
138 opt_list=getopt(cmd_line,'vH:ho:p:r:t:V',['debug','host=','help',
139 'optional=','port=','range=','timeout','version'])
140 for opt in opt_list[0]:
141 if opt[0]=='-v' or opt[0]=='--debug':
142 self.debug=1
143 elif opt[0]=='-H' or opt[0]=='--host':
144 self.host=opt[1]
145 elif opt[0]=='-h' or opt[0]=='--help':
146 doc_help()
147 self.exit_code=1 # request termination
148 break
149 elif opt[0]=='-o' or opt[0]=='--optional':
150 self.opt_ports=self.param2int_list(opt[1])
151 elif opt[0]=='-p' or opt[0]=='--port':
152 self.ports=self.param2int_list(opt[1])
153 elif opt[0]=='-r' or opt[0]=='--range':
154 r=string.replace(opt[1],':','-')
155 self.ranges=r
156 elif opt[0]=='-t' or opt[0]=='--timeout':
157 self.timeout=opt[1]
158 elif opt[0]=='-V' or opt[0]=='--version':
159 print self.Version()
160 self.exit_code=1 # request termination
161 break
162 else:
163 self.host=''
164 break
165
166 except:
167 # unknown param
168 self.host=''
169
170 if self.debug:
171 print 'Params:'
172 print '-------'
173 print 'host = %s' % self.host
174 print 'timeout = %s' % self.timeout
175 print 'ports = %s' % self.ports
176 print 'optional ports = %s' % self.opt_ports
177 print 'ranges = %s' % self.ranges
178 print
179
180 #
181 # a option that wishes us to terminate now has been given...
182 #
183 # This way, you can test params in debug mode and see what this
184 # program recognised by suplying a version param at the end of
185 # the cmd-line
186 #
187 if self.exit_code<>0:
188 sys.exit(self.UNKNOWN)
189
190 if self.host=='':
191 doc_syntax()
192 self.exit_code=self.UNKNOWN
193 self.exit_msg='UNKNOWN: bad params, try running without any params for syntax'
194
195
196 def CheckHost(self):
197 'Check one host using nmap.'
198 #
199 # Create a tmp file for storing nmap output
200 #
201 # The tempfile module from python 1.5.2 is stupid
202 # two processes runing at aprox the same time gets
203 # the same tempfile...
204 # For this reason I use a random suffix for the tmp-file
205 # Still not 100% safe, but reduces the risk significally
206 # I also inserted checks at various places, so that
207 # _if_ two processes in deed get the same tmp-file
208 # the only result is a normal error message to nagios
209 #
210 self.tmp_file=tempfile.mktemp('.%s') % random.randint(0,100000)
211 if self.debug:
212 print 'Tmpfile is: %s'%self.tmp_file
213 #
214 # If a range is given, only run nmap on this range
215 #
216 if self.ranges<>'':
217 global nmap_cmd # needed, to avoid error on next line
218 # since we assigns to nmap_cmd :)
219 nmap_cmd='%s -p %s' %(nmap_cmd,self.ranges)
220 #
221 # Prepare a task
222 #
223 t=utils.Task('%s %s' %(nmap_cmd,self.host))
224 #
225 # Configure a time-out handler
226 #
227 th=utils.TimeoutHandler(t.Kill, time_to_live=self.timeout,
228 debug=self.debug)
229 #
230 # Fork of nmap cmd
231 #
232 t.Run(detach=0, stdout=self.tmp_file,stderr='/dev/null')
233 #
234 # Wait for completition, error or timeout
235 #
236 nmap_exit_code=t.Wait(idlefunc=th.Check, interval=1)
237 #
238 # Check for timeout
239 #
240 if th.WasTimeOut():
241 self.exit_code=self.CRITICAL
242 self.exit_msg='CRITICAL - Plugin timed out after %s seconds' % self.timeout
243 return
244 #
245 # Check for exit status of subprocess
246 # Must do this after check for timeout, since the subprocess
247 # also returns error if aborted.
248 #
249 if nmap_exit_code <> 0:
250 self.exit_code=self.UNKNOWN
251 self.exit_msg='nmap program failed with code %s' % nmap_exit_code
252 return
253 #
254 # Read output
255 #
256 try:
257 f = open(self.tmp_file, 'r')
258 output=f.readlines()
259 f.close()
260 except:
261 self.exit_code=self.UNKNOWN
262 self.exit_msg='Unable to get output from nmap'
263 return
264
265 #
266 # Store open ports in list
267 # scans for lines where first word contains '/'
268 # and stores part before '/'
269 #
270 self.active_ports=[]
271 try:
272 for l in output:
273 if len(l)<2:
274 continue
275 s=string.split(l)[0]
276 if string.find(s,'/')<1:
277 continue
278 p=string.split(s,'/')[0]
279 if string.find(l,'open')>1:
280 self.active_ports.append(int(p))
281 except:
282 # failure due to strange output...
283 pass
284
285 if self.debug:
286 print 'Ports found by nmap: ',self.active_ports
287 #
288 # Filter out optional ports, we don't check status for them...
289 #
290 try:
291 for p in self.opt_ports:
292 self.active_ports.remove(p)
293
294 if self.debug and len(self.opt_ports)>0:
295 print 'optional ports removed:',self.active_ports
296 except:
297 # under extreame loads the remove(p) above failed for me
298 # a few times, this exception hanlder handles
299 # this bug-alike situation...
300 pass
301
302 opened=self.CheckOpen()
303 closed=self.CheckClosed()
304
305 if opened <>'':
306 self.exit_code=self.CRITICAL
307 self.exit_msg='PORTS CRITICAL - Open:%s Closed:%s'%(opened,closed)
308 elif closed <>'':
309 self.exit_code=self.WARNING
310 self.exit_msg='PORTS WARNING - Closed:%s'%closed
311 else:
312 self.exit_code=self.OK
313 self.exit_msg='PORTS ok - Only defined ports open'
314
315
316 #
317 # Compares requested ports on with actually open ports
318 # returns all open that should be closed
319 #
320 def CheckOpen(self):
321 opened=''
322 for p in self.active_ports:
323 if p not in self.ports:
324 opened='%s %s' %(opened,p)
325 return opened
326
327 #
328 # Compares requested ports with actually open ports
329 # returns all ports that are should be open
330 #
331 def CheckClosed(self):
332 closed=''
333 for p in self.ports:
334 if p not in self.active_ports:
335 closed='%s %s' % (closed,p)
336 return closed
337
338
339 def CleanUp(self):
340 #
341 # If temp file exists, get rid of it
342 #
343 if self.tmp_file<>'' and os.path.isfile(self.tmp_file):
344 try:
345 os.remove(self.tmp_file)
346 except:
347 # temp-file colition, some other process already
348 # removed the same file...
349 pass
350
351 #
352 # Show numerical exits as string in debug mode
353 #
354 if self.debug:
355 print 'Exitcode:',self.exit_code,
356 if self.exit_code==self.UNKNOWN:
357 print 'UNKNOWN'
358 elif self.exit_code==self.OK:
359 print 'OK'
360 elif self.exit_code==self.WARNING:
361 print 'WARNING'
362 elif self.exit_code==self.CRITICAL:
363 print 'CRITICAL'
364 else:
365 print 'undefined'
366 #
367 # Check if invalid exit code
368 #
369 if self.exit_code<-1 or self.exit_code>2:
370 self.exit_msg=self.exit_msg+' - undefined exit code (%s)' % self.exit_code
371 self.exit_code=self.UNKNOWN
372
373
374
375
376
377#
378# Help texts
379#
380def doc_head():
381 print """
382check_nmap plugin for Nagios
383Copyright (c) 2000 Jacob Lundqvist (jaclu@galdrion.com)
384License: GPL
385Version: %s""" % _version_
386
387
388def doc_syntax():
389 print """
390Usage: check_nmap.py [-v|--debug] [-H|--host host] [-V|--version] [-h|--help]
391 [-o|--optional port1,port2,port3 ...] [-r|--range range]
392 [-p|--port port1,port2,port3 ...] [-t|--timeout timeout]"""
393
394
395def doc_help():
396 'Help is displayed if run without params.'
397 doc_head()
398 doc_syntax()
399 print """
400Options:
401 -h = help (this screen ;-)
402 -v = debug mode, show some extra output
403 -H host = host to check (name or IP#)
404 -o ports = optional ports that can be open (one or more),
405 no warning is given if optional port is closed
406 -p ports = ports that should be open (one or more)
407 -r range = port range to feed to nmap. Example: :1024,2049,3000:7000
408 -t timeout = timeout in seconds, default 10
409 -V = Version info
410
411This plugin attempts to verify open ports on the specified host.
412
413If all specified ports are open, OK is returned.
414If any of them are closed, WARNING is returned (except for optional ports)
415If other ports are open, CRITICAL is returned
416
417If possible, supply an IP address for the host address,
418as this will bypass the DNS lookup.
419"""
420
421
422#
423# Main
424#
425if __name__ == '__main__':
426
427 if len (sys.argv) < 2:
428 #
429 # No params given, show syntax and exit
430 #
431 doc_syntax()
432 sys.exit(-1)
433
434 nmap=CheckNmap(sys.argv[1:])
435 exit_code,exit_msg=nmap.Run()
436
437 #
438 # Give Nagios a msg and a code
439 #
440 print exit_msg
441 sys.exit(exit_code)