Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env ruby
- DEBUG = ENV.fetch('CF_DEBUG', false)
- require File.join(File.dirname(__FILE__), '..', 'nitro', 'nitro-0.40', 'glycerin')
- require 'rubygems'
- # a specific facets version
- require_gem 'facets', '= 1.4.5'
- require 'facets'
- require 'socket'
- require 'time'
- require 'og'
- require 'glue/timestamped'
- Og.create_schema = false
- # class Logger
- #
- # Usage:
- #
- # Logger.debug("important!")
- #
- # prints "DEBUG important!" to $stdout
- class Logger
- # creates a new method on method missing
- def self.method_missing(sym, *args)
- define_method(sym) do |a|
- $stderr.puts "#{sym.to_s.upcase} #{a.join(', ')}"
- end
- end
- end
- # ClamScanner
- #
- # Usage:
- #
- # clamav = ClamScanner.new
- # clamav.scan("/home/asdf/file.rb")
- class ClamScanner
- class ScannerException < RuntimeError
- def initialize(message)
- Logger.error(message)
- end
- end
- attr_accessor :socket, :clam, :result
- STAT = {
- 0 => "No virus found.",
- 1 => "Virus(es) found.",
- 40 => "Unknown option passed.",
- 50 => "Database initialization error.",
- 52 => "Not supported file type.",
- 53 => "Can't open directory.",
- 54 => "Can't open file. (ofm)",
- 55 => "Error reading file. (ofm)",
- 56 => "Can't stat input file / directory.",
- 57 => "Can't get absolute path name of current working directory.",
- 58 => "I/O error, please check your file system.",
- 59 => "Can't get information about current user from /etc/passwd.",
- 60 => "Can't get information about user 'clamav' (default name) from /etc/passwd.",
- 61 => "Can't fork.",
- 62 => "Can't initialize logger.",
- 63 => "Can't create temporary files/directories (check permissions).",
- 64 => "Can't write to temporary directory (please specify another one).",
- 70 => "Can't allocate and clear memory (calloc).",
- 71 => "Can't allocate memory (malloc)."
- }
- def initialize
- @socket = '/var/run/clamav/clamd.sock'
- @clam = '/home/admispconfig/ispconfig/tools/clamav/bin/clamscan'
- end
- def check
- res = talk("PING")
- if(res !~ /PONG$/)
- Logger.error("check failed: erroneous clam answer: #{res}")
- return false
- end
- return true
- end
- def scan(path)
- if File.exist?(@socket)
- scand(path)
- else
- scanl(path)
- end
- end
- def scanl(path)
- res = talk_local(path)
- Logger.info(STAT[res])
- @result = STAT[res]
- end
- def scand(path)
- begin
- res = talk("SCAN #{path}")
- rescue ScannerException
- Logger.error("unable to scan message: #{path}")
- return nil
- end
- if res =~ /: (.*) FOUND$/
- Logger.info("virus: #{$1} in: #{path}")
- @result = $1;
- elsif res =~ /: (Zip module failure) ERROR$/
- Logger.info("broken zip in #{path}")
- @result = 'Broken.ZIP.Archive'
- elsif res =~ /OK$/
- Logger.info("no virus detected in: #{path}")
- @result = STAT[0]
- else
- Logger.error("unknown clam resource problem: #{$1}: when
- scanning: #{path}")
- return nil
- end
- return @result
- end # end scan
- def talk(msg)
- data = nil
- session = nil
- if msg.nil?
- raise ScannerException.new("method called with empty args")
- end
- begin
- session = UNIXSocket.new(@socket)
- session.send(msg,0)
- data = session.recvfrom(512)[0] # [0] ensures we only get clam's respons
- session.close()
- rescue
- raise ScannerException.new("unable to use socket: #{@socket}")
- end
- return data
- end # end talk
- def talk_local(msg)
- system @clam, '--quiet', '--stdout', msg
- res = $?.to_i
- end
- end
- class ClamavScanResult
- is Timestamped
- property :path, String
- property :result, String
- end
- # UploadedFile
- #
- # Facility to encapsulate data for a uploaded file.
- # Right now, only /var/log/xferlog style parsing is supported. Can be used
- # for other styles as well though.
- #
- # Usage:
- #
- # uf = UploadedFile.parse_xferlog(line)
- # uf.filename # => '/home/asdf/file.rb'
- class UploadedFile
- attr_reader :options
- attr_reader :date, :transfertime, :remotecomputer, :filesize, :filename, :transfertype, :actionflag, :direction, :accessmode, :username, :servicename, :authentication, :id, :complete
- # Creates a new UploadedFile instance
- #
- # UploadedFile.new(hsh)
- #
- # [ options ]
- # A Hash which is transformed into instance variables
- #
- # Example:
- #
- # uf = UploadedFile.new(:filename => 'asdf.rb')
- # uf.filename # => 'asdf.rb'
- def initialize(options)
- @options = options
- options.each do |key, value|
- instance_variable_set("@#{key}", value)
- end
- end
- # Parses a /var/log/xferlog style file
- #
- #
- # Following is logged in /var/log/xferlog
- #
- # * current time
- # * transfer time in seconds
- # * remote computer name
- # * file size
- # * file name
- # * transfer type ("a": Ascii; "b": Binary)
- # * action flag ("C": compressed; "U": uncompressed; "T": tarball;
- # "_": unknown)
- # * direction ("o": outgoing; "i": incoming, "d": delete)
- # * access mode ("a": anonymous; "r": existing user)
- # * username (when 'anonymous', this is the given password)
- # * service name (normally "FTP")
- # * authentication method ("0": none; "1": RFC931 authentication)
- # * ident ("*": when no ident given)
- # * complete ("c": comlete; "i": not complete). Notde: uploads are
- # always "c", for ProFTPD doesn't know when the upload is complete
- #
- # Example:
- #
- # Tue Dec 12 16:40:20 2006 0 port.dynamic.qsc.de 245 /home/asdf/build/.autotest b _ i r asdf ftp 1 * c
- # Tue Dec 12 08:57:07 2006 0 10.0.0.102 3011 /srv/261FS1.jpg b _ i r webmaster ftp 1 JiJI c
- def self.parse_xferlog(line)
- line.strip!
- date, transfertime, remotecomputer, filesize, filename, transfertype, actionflag, direction, accessmode, username, servicename, authentication, id, complete =
- line.scan(/
- (\w \s \w \s \d \s \d :\d :\d \s\d )\s # date
- (\d )\s # transfertime
- ([^\s] )\s # remotecomputer
- (\d )\s # filesize
- (. ?)\s # filename
- ([ab])\s # transfertype
- ([CUT_])\s # actionflag
- ([oid])\s # direction
- ([ar])\s # accessmode
- ([^\s] )\s # username
- (\w )\s # servicename
- ([01])\s # authentication
- ([^\s] )\s # id
- ([ci]) # complete
- /mix).first
- date = Time.parse(date)
- transfertime = transfertime.to_i
- filesize = filesize.to_i
- complete = (complete == 'c' ? true : false)
- direction = case direction
- when 'o': :out
- when 'i': :in
- when 'd': :delete
- end
- options = {
- :date => date,
- :transfertime => transfertime,
- :remotecomputer => remotecomputer,
- :filesize => filesize,
- :filename => filename,
- :transfertype => transfertype,
- :actionflag => actionflag,
- :direction => direction,
- :accessmode => accessmode,
- :username => username,
- :servicename => servicename,
- :authentication => authentication,
- :id => id,
- :complete => complete
- }
- UploadedFile.new(options)
- rescue StandardError => ex
- Logger.error ex.message
- Logger.error line
- end
- end
- # Initialize database
- Og.start(:destroy => false, :evolve_schema => :add, :store => :postgresql,
- :name => 'clamavdb', :user => 'BGB1-Jemenit',
- :password => '3', :classes => [ClamavScanResult])
- # Run as:
- #
- # tail -f /var/log/xferlog | ./clamav_og.rb
- #
- # Output are logger statements, Virus checks are also added to database
- if $0 == __FILE__
- clamav = ClamScanner.new
- # While stdin can be read (blocks, when no more data is available)
- while io = select([$stdin])
- next unless io[0][0].kind_of?(IO) rescue nil
- # read one line from stdin
- line = io[0][0].readline.chomp
- # parse that line
- file = UploadedFile.parse_xferlog(line)
- # only scan incoming files
- next unless file.direction == :in
- # scan the file
- res = clamav.scan(file.filename)
- # add scan to database
- ClamavScanResult.create_with(:path => file.filename, :result => res,
- :update_time => file.transfertime)
- Logger.notify "added result"
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement