Guest User

Untitled

a guest
Feb 28th, 2018
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.71 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2.  
  3. DEBUG = ENV.fetch('CF_DEBUG', false)
  4.  
  5. require File.join(File.dirname(__FILE__), '..', 'nitro', 'nitro-0.40', 'glycerin')
  6. require 'rubygems'
  7.  
  8. # a specific facets version
  9. require_gem 'facets', '= 1.4.5'
  10. require 'facets'
  11.  
  12. require 'socket'
  13. require 'time'
  14.  
  15. require 'og'
  16. require 'glue/timestamped'
  17.  
  18. Og.create_schema = false
  19.  
  20.  
  21. # class Logger
  22. #
  23. # Usage:
  24. #
  25. # Logger.debug("important!")
  26. #
  27. # prints "DEBUG important!" to $stdout
  28.  
  29. class Logger
  30.  
  31. # creates a new method on method missing
  32. def self.method_missing(sym, *args)
  33. define_method(sym) do |a|
  34. $stderr.puts "#{sym.to_s.upcase} #{a.join(', ')}"
  35. end
  36. end
  37. end
  38.  
  39. # ClamScanner
  40. #
  41. # Usage:
  42. #
  43. # clamav = ClamScanner.new
  44. # clamav.scan("/home/asdf/file.rb")
  45.  
  46. class ClamScanner
  47. class ScannerException < RuntimeError
  48. def initialize(message)
  49. Logger.error(message)
  50. end
  51. end
  52.  
  53. attr_accessor :socket, :clam, :result
  54.  
  55. STAT = {
  56. 0 => "No virus found.",
  57. 1 => "Virus(es) found.",
  58. 40 => "Unknown option passed.",
  59. 50 => "Database initialization error.",
  60. 52 => "Not supported file type.",
  61. 53 => "Can't open directory.",
  62. 54 => "Can't open file. (ofm)",
  63. 55 => "Error reading file. (ofm)",
  64. 56 => "Can't stat input file / directory.",
  65. 57 => "Can't get absolute path name of current working directory.",
  66. 58 => "I/O error, please check your file system.",
  67. 59 => "Can't get information about current user from /etc/passwd.",
  68. 60 => "Can't get information about user 'clamav' (default name) from /etc/passwd.",
  69. 61 => "Can't fork.",
  70. 62 => "Can't initialize logger.",
  71. 63 => "Can't create temporary files/directories (check permissions).",
  72. 64 => "Can't write to temporary directory (please specify another one).",
  73. 70 => "Can't allocate and clear memory (calloc).",
  74. 71 => "Can't allocate memory (malloc)."
  75. }
  76.  
  77. def initialize
  78. @socket = '/var/run/clamav/clamd.sock'
  79. @clam = '/home/admispconfig/ispconfig/tools/clamav/bin/clamscan'
  80. end
  81.  
  82. def check
  83. res = talk("PING")
  84. if(res !~ /PONG$/)
  85. Logger.error("check failed: erroneous clam answer: #{res}")
  86. return false
  87. end
  88. return true
  89. end
  90.  
  91. def scan(path)
  92. if File.exist?(@socket)
  93. scand(path)
  94. else
  95. scanl(path)
  96. end
  97. end
  98.  
  99. def scanl(path)
  100. res = talk_local(path)
  101. Logger.info(STAT[res])
  102. @result = STAT[res]
  103. end
  104.  
  105. def scand(path)
  106. begin
  107. res = talk("SCAN #{path}")
  108. rescue ScannerException
  109. Logger.error("unable to scan message: #{path}")
  110. return nil
  111. end
  112.  
  113. if res =~ /: (.*) FOUND$/
  114. Logger.info("virus: #{$1} in: #{path}")
  115. @result = $1;
  116. elsif res =~ /: (Zip module failure) ERROR$/
  117. Logger.info("broken zip in #{path}")
  118. @result = 'Broken.ZIP.Archive'
  119. elsif res =~ /OK$/
  120. Logger.info("no virus detected in: #{path}")
  121. @result = STAT[0]
  122. else
  123. Logger.error("unknown clam resource problem: #{$1}: when
  124. scanning: #{path}")
  125. return nil
  126. end
  127.  
  128. return @result
  129. end # end scan
  130.  
  131. def talk(msg)
  132. data = nil
  133. session = nil
  134.  
  135. if msg.nil?
  136. raise ScannerException.new("method called with empty args")
  137. end
  138. begin
  139. session = UNIXSocket.new(@socket)
  140. session.send(msg,0)
  141. data = session.recvfrom(512)[0] # [0] ensures we only get clam's respons
  142. session.close()
  143. rescue
  144. raise ScannerException.new("unable to use socket: #{@socket}")
  145. end
  146. return data
  147. end # end talk
  148.  
  149. def talk_local(msg)
  150. system @clam, '--quiet', '--stdout', msg
  151. res = $?.to_i
  152. end
  153.  
  154. end
  155.  
  156. class ClamavScanResult
  157. is Timestamped
  158.  
  159. property :path, String
  160. property :result, String
  161. end
  162.  
  163.  
  164. # UploadedFile
  165. #
  166. # Facility to encapsulate data for a uploaded file.
  167. # Right now, only /var/log/xferlog style parsing is supported. Can be used
  168. # for other styles as well though.
  169. #
  170. # Usage:
  171. #
  172. # uf = UploadedFile.parse_xferlog(line)
  173. # uf.filename # => '/home/asdf/file.rb'
  174.  
  175. class UploadedFile
  176.  
  177. attr_reader :options
  178. attr_reader :date, :transfertime, :remotecomputer, :filesize, :filename, :transfertype, :actionflag, :direction, :accessmode, :username, :servicename, :authentication, :id, :complete
  179.  
  180. # Creates a new UploadedFile instance
  181. #
  182. # UploadedFile.new(hsh)
  183. #
  184. # [+options+]
  185. # A Hash which is transformed into instance variables
  186. #
  187. # Example:
  188. #
  189. # uf = UploadedFile.new(:filename => 'asdf.rb')
  190. # uf.filename # => 'asdf.rb'
  191.  
  192. def initialize(options)
  193. @options = options
  194. options.each do |key, value|
  195. instance_variable_set("@#{key}", value)
  196. end
  197. end
  198.  
  199. # Parses a /var/log/xferlog style file
  200. #
  201. #
  202. # Following is logged in /var/log/xferlog
  203. #
  204. # * current time
  205. # * transfer time in seconds
  206. # * remote computer name
  207. # * file size
  208. # * file name
  209. # * transfer type ("a": Ascii; "b": Binary)
  210. # * action flag ("C": compressed; "U": uncompressed; "T": tarball;
  211. # "_": unknown)
  212. # * direction ("o": outgoing; "i": incoming, "d": delete)
  213. # * access mode ("a": anonymous; "r": existing user)
  214. # * username (when 'anonymous', this is the given password)
  215. # * service name (normally "FTP")
  216. # * authentication method ("0": none; "1": RFC931 authentication)
  217. # * ident ("*": when no ident given)
  218. # * complete ("c": comlete; "i": not complete). Notde: uploads are
  219. # always "c", for ProFTPD doesn't know when the upload is complete
  220. #
  221. # Example:
  222. #
  223. # Tue Dec 12 16:40:20 2006 0 port.dynamic.qsc.de 245 /home/asdf/build/.autotest b _ i r asdf ftp 1 * c
  224. # Tue Dec 12 08:57:07 2006 0 10.0.0.102 3011 /srv/261FS1.jpg b _ i r webmaster ftp 1 JiJI c
  225.  
  226. def self.parse_xferlog(line)
  227. line.strip!
  228. date, transfertime, remotecomputer, filesize, filename, transfertype, actionflag, direction, accessmode, username, servicename, authentication, id, complete =
  229. line.scan(/
  230. (\w+\s+\w+\s+\d+\s+\d+:\d+:\d+\s\d+)\s+ # date
  231. (\d+)\s+ # transfertime
  232. ([^\s]+)\s+ # remotecomputer
  233. (\d+)\s+ # filesize
  234. (.+?)\s+ # filename
  235. ([ab])\s+ # transfertype
  236. ([CUT_])\s+ # actionflag
  237. ([oid])\s+ # direction
  238. ([ar])\s+ # accessmode
  239. ([^\s]+)\s+ # username
  240. (\w+)\s+ # servicename
  241. ([01])\s+ # authentication
  242. ([^\s]+)\s+ # id
  243. ([ci]) # complete
  244. /mix).first
  245.  
  246. date = Time.parse(date)
  247. transfertime = transfertime.to_i
  248. filesize = filesize.to_i
  249. complete = (complete == 'c' ? true : false)
  250. direction = case direction
  251. when 'o': :out
  252. when 'i': :in
  253. when 'd': :delete
  254. end
  255.  
  256. options = {
  257. :date => date,
  258. :transfertime => transfertime,
  259. :remotecomputer => remotecomputer,
  260. :filesize => filesize,
  261. :filename => filename,
  262. :transfertype => transfertype,
  263. :actionflag => actionflag,
  264. :direction => direction,
  265. :accessmode => accessmode,
  266. :username => username,
  267. :servicename => servicename,
  268. :authentication => authentication,
  269. :id => id,
  270. :complete => complete
  271. }
  272.  
  273. UploadedFile.new(options)
  274. rescue StandardError => ex
  275. Logger.error ex.message
  276. Logger.error line
  277. end
  278. end
  279.  
  280. # Initialize database
  281.  
  282. Og.start(:destroy => false, :evolve_schema => :add, :store => :postgresql,
  283. :name => 'clamavdb', :user => 'BGB1-Jemenit',
  284. :password => '3', :classes => [ClamavScanResult])
  285.  
  286. # Run as:
  287. #
  288. # tail -f /var/log/xferlog | ./clamav_og.rb
  289. #
  290. # Output are logger statements, Virus checks are also added to database
  291.  
  292. if $0 == __FILE__
  293. clamav = ClamScanner.new
  294.  
  295. # While stdin can be read (blocks, when no more data is available)
  296. while io = select([$stdin])
  297. next unless io[0][0].kind_of?(IO) rescue nil
  298.  
  299. # read one line from stdin
  300. line = io[0][0].readline.chomp
  301.  
  302. # parse that line
  303. file = UploadedFile.parse_xferlog(line)
  304.  
  305. # only scan incoming files
  306. next unless file.direction == :in
  307.  
  308. # scan the file
  309. res = clamav.scan(file.filename)
  310.  
  311. # add scan to database
  312. ClamavScanResult.create_with(:path => file.filename, :result => res,
  313. :update_time => file.transfertime)
  314. Logger.notify "added result"
  315. end
  316.  
  317. end
Add Comment
Please, Sign In to add comment