Your IP : 216.73.216.220


Current Path : /opt/microsoft/omsagent/plugin/
Upload File :
Current File : //opt/microsoft/omsagent/plugin/auditlog_lib.rb

module AuditLogModule
  class LoggingBase
    def log_error(text)
    end
  end

  class RuntimeError < LoggingBase
    def log_error(text)
      $log.error "RuntimeError: #{text}"
    end
  end

  class AuditLogParser
    require 'date'
    require 'etc'
    require 'digest'
    require_relative 'oms_common'

    def initialize(error_handler)
      @error_handler = error_handler
      @sha256 = Digest::SHA256.new
    end

    SKETCH_REGEX = /type=(?<record>[A-Z_]+) msg=audit\((?<audit_id>(?<time_epoch>[0-9\.]+):(?<serial_number>[0-9]+))\): (?<additional_fields>.+)/
    ADDITIONAL_MSG_REGEX = /(?<additional_msg>^[^=]+) [a-z0-1_\-]+=/
    FIELDS_REGEX = /(msg=')?([a-z0-9_\-]+)=("[^"]*"|'[^']*'|[^ ]*)?/

    def parse(line)
      data = {}
      time = Time.now.to_f

      begin
        SKETCH_REGEX.match(line) { |match|
          data['Computer'] = OMS::Common.get_hostname
          data['RecordType'] = match['record']
          data['AuditID'] = match['audit_id']
          time = match['time_epoch'].to_f
          data['Timestamp'] = OMS::Common.format_time(time)
          data['SerialNumber'] = match['serial_number']
          additional_fields = match['additional_fields']
        
          # process the message before the first field
          ADDITIONAL_MSG_REGEX.match(additional_fields) { |m|
            data['AdditionalMessage'] = m['additional_msg']
          }
        
          additional_fields.scan(FIELDS_REGEX) { |m|
            name = m[1]
            value = strip_quotes(m[2])
            data[name] = get_typed_value(name, value)
            friendly_name, readable_value = translate(name, value)
            data[friendly_name] = readable_value if !readable_value.nil?
          }
          data['Hash'] = @sha256.hexdigest line
        }
      rescue => e
        @error_handler.log_error("Unable to parse the line #{e}")
      end

      return time, data
    end
    

    private

    def strip_quotes(value)
      if value.start_with?('"') or value.start_with?("'")
        value = value[1..-1]
      end
    
      if value.end_with?('"') or value.end_with?("'")
        value = value[0..-2]
      end
    
      return value
    end
    
    USER_ID_FIELDS_MAPPING = {
      'uid' => 'user_name',
      'auid' => 'audit_user',
      'euid' => 'effective_user',
      'suid' => 'set_user',
      'fsuid' => 'filesystem_user',
      'inode_uid' => 'inode_user',
      'oauid' => 'o_audit_user',
      'ouid' => 'o_user_name',
      'obj_uid' => 'obj_user',
      'sauid' => 'sender_audit_user'
    }
    
    GROUP_ID_FIELDS_MAPPING = {
      'gid' => 'group_name',
      'egid' => 'effective_group',
      'fsgid' => 'filesystem_group',
      'inode_gid' => 'inode_group',
      'new_gid' => 'new_group',
      'obj_gid' => 'obj_group',
      'ogid' => 'owner_group',
      'sgid' => 'set_group'
    }
    
    PROCESS_ID_FIELDS_MAPPING = {
      'pid' => 'process_name',
      'opid' => 'o_process_name',
      'ppid' => 'parent_process'
    }

    def translate(name, value)
      begin
        if PROCESS_ID_FIELDS_MAPPING.has_key? name
          return PROCESS_ID_FIELDS_MAPPING[name], get_process_name_by_id(value)
        end
      
        if USER_ID_FIELDS_MAPPING.has_key? name
          return USER_ID_FIELDS_MAPPING[name], get_user_name_by_id(value.to_i)
        end
      
        if GROUP_ID_FIELDS_MAPPING.has_key? name
          return GROUP_ID_FIELDS_MAPPING[name], get_group_name_by_id(value.to_i)
        end
      rescue => e
        # type doesn't match
        @error_handler.log_error("Unable to translate the field: '#{name}': '#{value}'. Message: #{e}")
      end
    end
    
    def get_typed_value(name, value)
      # determine the type of the value by name and convert
      return value
    end
    
    def get_process_name_by_id(pid)
      begin
        return IO.read("/proc/#{pid}/cmdline").tr("\0", ' ').strip
      rescue
        # Process terminated
      end
    end
    
    def get_user_name_by_id(uid)
      return 'unset' if uid == 0xffffffff
    
      begin
        return Etc.getpwuid(uid).name
      rescue
        # User not exists
      end
    end
    
    def get_group_name_by_id(gid)
      return 'unset' if gid == 0xffffffff
    
      begin
        return Etc.getgrgid(gid).name
      rescue
        # Group not exists
      end
    end

  end
end