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